CountIFs in Excel with VBA is not counting decimal values - excel

I have a problem with the function CountIF when used with decimals.
Below the code I have:
Sub Compair()
Dim I As Double
Row = 3
For I = 139.5 To 141.5 Step 0.25
Cells(Row, 3) = I
Cells(Row, 4) = Application.WorksheetFunction.CountIf(Range("A:A"), "<" & Cells(Row, 3))
Row = Row + 1
Next I
End Sub
And below the output:
It seems that the code functions good when it is compairing integer value and not with decimals.
PS: I do not want to loop on column A cells, as there could be more than 100k values and it will be so slowly

You can pull everything into variant arrays, which will be quicker on larger datasets than COUNTIFS:
Sub Compair()
With ActiveSheet
Dim rngArr As Variant
rngArr = Intersect(.UsedRange, .Range("A2", .Cells(.Rows.Count, 1))).Value2
Dim outArr() As Variant
ReDim outArr(1 To Int((141.5 - 139.5) / 0.25) + 1, 1 To 2)
Dim I As Double
Row = 1
For I = 139.5 To 141.5 Step 0.25
outArr(Row, 1) = I
outArr(Row, 2) = 0
Dim j As Long
For j = 1 To UBound(rngArr, 1)
If rngArr(j, 1) < I Then outArr(Row, 2) = outArr(Row, 2) + 1
Next j
Row = Row + 1
Next I
.Range("C3").Resize(UBound(outArr, 1), 2).Value = outArr
End With
End Sub

Your code is correct
just confirm your region, if you need the comas as decimal separatos configure your Windows format, and your excel format
Windows Format
https://www.windowscentral.com/how-change-date-and-time-formats-windows-10
Excel Format
https://edu.gcfglobal.org/en/excel2013/formatting-cells/1/
Sub Compair()
Range("A16").Select
Dim I As Double
Row = 16
For I = 139.5 To 141.5 Step 0.25
Cells(Row, 3) = I
Cells(Row, 4) = Application.WorksheetFunction.CountIf(Range("A:A"), "<" & Cells(Row, 3))
Row = Row + 1
Next I
End Sub

Related

VBA SumIfs Too Slow

I have a WorksheetFunction.SumIfs with 3 Args code being applied in so many cells (10k rows x 20 columns), it ran for 2 hours to get complete, but when I do the same but with formula in excel and drag and drop until last column and line, it goes much faster (less than 10min).
I have already done xlCalculationManual. do you have any idea on how to improve processing time in VBA?
Code:
application.calculation= xlCalculationManual
for Col = 3 to 22
for Row = 2 to 10000
FileA.Cells(Row, Col).Value = Application.WorksheetFunction.SumIfs(FileB.Range("A:A"), FileB.Range("D:D"), FileA.Range("A" & Row).Value, FileB.Range("B:B"), FileA.Range("B" & Row).Value, FileB.Range("C:C"), FileA.Cells(1, Col).Value)
Next
Next
SOLUTION:
I found a simple solution by myself. In a big range of data, instead of using Application.WorksheetFunction.FUNCTION_NAME inside FOR, use Book.Sheet.Range().Formula = "=Formula(Parameters)" in the first Cell, then use .Copy, then .PasteSpecial Paste:=xlPasteFormulas, examples below:
' Takes 2h
for Col = 3 to 22
for Row = 2 to 10000
FileA.Cells(Row, Col).Value = Application.WorksheetFunction.SumIfs(FileB.Range("A:A"), FileB.Range("D:D"), FileA.Range("A" & Row).Value, FileB.Range("B:B"), FileA.Range("B" & Row).Value, FileB.Range("C:C"), FileA.Cells(1, Col).Value)
Next
Next
' Takes 10min
application.calculation= xlCalculationManual
FileA.Cells(2, 3).Formula = "=SUMIFS([FileB.XLSX]Sheet1!$A:$A,[FileB.XLSX]Sheet1!$D:$D,$A2,[FileB.XLSX]Sheet1!$B:$B,$B2,[FileB.XLSX]Sheet1!$C:$C,C$1)"
FileA.Cells(2, 3).Copy
FileA.Range(FileA.Cells(2, 3), FileA.Cells(10000, 22)).PasteSpecial Paste:=xlPasteFormulas
application.calculation= xlCalculationAutomatic
As per my comments, use variant arrays and loop the range once.
Sub mysumif()
Dim fileA As Worksheet
Set fileA = Worksheets("Sheet2")
Dim fileB As Worksheet
Set fileB = Worksheets("Sheet1")
Dim rngArr As Variant
rngArr = Intersect(fileB.Range("A:D"), fileB.UsedRange)
Dim Bclm As Variant
Bclm = Intersect(fileA.Range("A2:B100000"), fileA.UsedRange)
Dim ttlRos As Variant
ttlRos = Intersect(fileA.Range("C1:ZZ1"), fileA.UsedRange)
Dim otptArr As Variant
ReDim otptArr(1 To UBound(Bclm, 1), 1 To UBound(ttlRos, 2))
Dim i As Long
For i = 1 To UBound(rngArr, 1)
Dim j As Variant
j = Application.Match(rngArr(i, 3), ttlRos, 0)
If Not IsError(j) Then
Dim k As Long
For k = 1 To UBound(Bclm, 1)
If Bclm(k, 1) = rngArr(i, 4) And Bclm(k, 2) = rngArr(i, 2) Then
otptArr(k, j) = otptArr(k, j) + rngArr(i, 1)
Exit For
End If
Next k
End If
Next i
fileA.Range("C2").Resize(UBound(otptArr, 1), UBound(otptArr, 2)).Value = otptArr
End Sub
Before:
After:
Also note that a pivot table can do this also much quicker:

How do I convert the Excel formula =STDEV.S(IF(FREQUENCY(range,range),range)) into VBA code?

I have an Excel formula that operates on a pre-existing range of data.
The Excel formula is: =STDEV.S(IF(FREQUENCY(range,range),range)) , where "range" is the aforementioned range of data.
My goal is to convert this formula into VBA code.
The following code is my attempt at trying to convert the formula into VBA, as well as my visualization of the process to try and understand why it is not putting out the same result:
Private Sub CommandButton1_Click()
Dim diffArray() As Variant
Dim i As Integer
Dim x As Integer
Dim array1() As Variant, size As Integer, j As Integer
Dim freqArray1() As Variant
Dim freqArray2() As Variant, size2 As Integer, j2 As Integer
'assigns the data values to array1
size = 0
j = 0
ReDim array1(size)
For i = 3 To 15
size = size + 1
ReDim Preserve array1(size)
array1(j) = Cells(i, 2)
j = j + 1
Next i
Cells(20, 2).Value = UBound(array1)
Cells(21, 2).Value = LBound(array1)
If UBound(array1) > 1 Then Cells(19, 2).Value = WorksheetFunction.StDev_S(array1)
'setting freqArray1 to frequency(array1, array1)
freqArray1 = WorksheetFunction.Frequency(array1, array1)
Cells(20, 3).Value = UBound(freqArray1)
Cells(21, 3).Value = LBound(freqArray1)
For i = LBound(freqArray1) To (UBound(freqArray1))
Cells(2 + LBound(freqArray1) + i, 3).Value = freqArray1(i, 1)
Next i
If UBound(freqArray1) > 1 Then Cells(19, 3).Value = WorksheetFunction.StDev_S(freqArray1)
'setting freqArray2 to if(frequency(array1, array1), array1)
size2 = 0
j2 = 0
ReDim freqArray2(size2)
For i = LBound(freqArray1) To (UBound(freqArray1))
If freqArray1(i, 1) Then
size2 = size2 + 1
ReDim Preserve freqArray2(size2)
freqArray2(j2) = freqArray1(i, 1)
j2 = j2 + 1
End If
Next i
Cells(20, 4).Value = UBound(freqArray2)
Cells(21, 4).Value = LBound(freqArray2)
For i = (LBound(freqArray2)) To UBound(freqArray2)
Cells(2 + LBound(freqArray2) + i, 4).Value = freqArray2(i)
Next i
'takes the standard deviation of if(frequency(array1, array1), array1)
If UBound(freqArray2) > 1 Then Cells(19, 4).Value = WorksheetFunction.StDev_S(freqArray2)
End Sub
The data values being operated on are in the orange cells column B(array1).
The array 'frequency(array1, array1)' is in the yellow cells column C.
The array 'if(frequency(array1, array1), array1)' is in the green cells column D.
The goal is for the values in the two blue cells(B18 and D19) to be the same.
I don't understand two things:
Why are the values in the blue cells(B18 and D19) not the same?
Why do the indices of the arrays change?
One starts at '0', the next starts at '1', and the last starts at '-1'?
use a dictionary to create a unique list and use that in the StDev_S
Private Sub CommandButton1_Click()
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
Dim rngArray As Variant
rngArray = ActiveSheet.Range("B3:B15")
Dim i As Long
For i = LBound(rngArray, 1) To UBound(rngArray, 1)
On Error Resume Next
dict.Add rngArray(i, 1), rngArray(i, 1)
On Error Resume Next
Next i
If dict.Count > 0 Then
Dim unqArr As Variant
ReDim unqArr(1 To dict.Count) As Variant
i = 1
Dim key As Variant
For Each key In dict.Keys
unqArr(i) = key
i = i + 1
Next key
ActiveSheet.Cells(19, 4).Value = Application.WorksheetFunction.StDev_S(unqArr)
End If
End Sub

How to duplicate preferred columns data in Conditionally one sheet to multiple sheets

In My office five Employee is working for example In my office Employ Entry Exit sheet is dere..
This is Main Sheet
Now my requirement
category wise data copy to this sheet to other sheet but it's do it automatically
Like Example
enter image description here
I hope I am interpreting your question correctly, but please let me know if I have misinterpreted your request.
Try the following code on your sheet:
Sub AutoCopyByName()
Dim Names() As String
Dim i As Long, NumRows As Long, NameRow() As Long
Dim j As Integer, NumNames As Integer
j = 0
NumSites = 0
'''''''''''''''''''''''''''''''''''''''''''
'''COUNT NUMBER OF ROWS WITH INFORMATION'''
'''''''''''''''''''''''''''''''''''''''''''
i = 2 'Standard Counter (counts all non-blank cells)
NumRows = 1 'Number of rows with information
Do While WorksheetFunction.IsText(Sheets("data").Range("A" & i))
If Sheets("data").Range("A" & i) <> " " Then NumRows = NumRows + 1
i = i + 1
Loop
'''''''''''''''''''''''''''
'''COUNT NUMBER OF NAMES'''
'''''''''''''''''''''''''''
For i = 3 To NumRows + 1
If Sheets("data").Cells(i, 1) <> Sheets("data").Cells(i - 1, 1) Then NumNames = NumNames + 1 'Works
Next i
''''''''''''''''''
'''REDIM ARRAYS'''
''''''''''''''''''
ReDim Names(NumNames)
ReDim NameRow(NumNames)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'''FINDING THE LOCATION OF EACH NAME IN THE SHEET AND STORING IT IN NameRow ARRAY'''
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
For i = 2 To NumRows + 1
If Sheets("data").Cells(i, 1) <> Sheets("data").Cells(i - 1, 1) Then
Names(j) = Sheets("data").Cells(i, 1).Value
NameRow(j) = i
j = j + 1
End If
Next i
'''''''''''''''''''''''''''''''''''''''''
'''COPY ENTRIES PER NAME TO EACH SHEET'''
'''''''''''''''''''''''''''''''''''''''''
For i = 0 To NumNames - 1
Worksheets.Add
Worksheets(1).Name = Names(i)
Worksheets("data").Rows(1).Copy
Worksheets(Names(i)).Paste
Worksheets("data").Activate
Worksheets("data").Range(Cells(NameRow(i), 1), Cells(NameRow(i + 1) - 1, 1)).EntireRow.Copy
Worksheets(Names(i)).Activate
Worksheets(Names(i)).Range("A2").Select
Worksheets(Names(i)).Paste
Next i
End Sub
I've used the following as my input sheet

Calculate from an array in memory

I am fairly new to VBA but understand the basics. My question is as follows:
I need to divide the individual cells of an array with its corresponding offset cell (E3/E2, F3/F2, G3/G2, etc.) and store it in an array. Then, I need to find the 1st, 2nd, and 3rd smallest numbers of that array and highlight the cell in the first row of that column. Here is what I have:
Option Base 1
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
'Private Sub test5()
Dim row As Integer
Dim column As Integer
Dim myArray(10) As Double
Dim myArray1(3) As String
Dim a As Long
Dim b As Long
Dim intQuizNumber As Integer
Dim intTestNumber As Integer
Dim intProjectNumber As Integer
intQuizNumber = 3
intTestNumber = 3
intProjectNumber = 3
On Error Resume Next
If Not Intersect(Target, Range(Range("D3"), Range("D3").End(xlDown))) Is Nothing Then
Range("1:1").Interior.Color = xlNone
row = ActiveCell.row
column = ActiveCell.column
For a = 1 To 10
myArray(a) = Cells(row, column + 1) / Cells(2, column + 1)
column = column + 1
Next a
row = ActiveCell.row
column = ActiveCell.column
'Evaluate("=RANK(E3,$E$3:$N$3,0)+COUNTIF($E$3:E3,E3)-1")
For b = 1 To 3
myArray1(b) = Evaluate("=CELL(""address"",OFFSET(" & Target.Offset(0, 1).Address & ",0,MATCH(SMALL(" & Target.Offset(0, 1).Address & ":" & Target.Offset(0, 3 + 3 + 3 + 1).Address & "," & b & ")," & Target.Offset(0, 1).Address & ":" & Target.Offset(0, 3 + 3 + 3 + 1).Address & ",0)-1))")
Next b
Union(Range(myArray1(1)).Offset(-row + 1, 0), Range(myArray1(2)).Offset(-row + 1, 0), Range(myArray1(3)).Offset(-row + 1, 0)).Interior.Color = 65535
Else
Range("1:1").Interior.Color = xlNone
End If
End Sub
I would like to replace the Evaluate statement in "b" loop with the one that I have commented out but can't seem to do it. I first need the value of the division and then I need to get the three lowest and highlight the cells. I've searched on Google thoroughly and can't figure this out. Any help would be greatly appreciated!!
Thank You
I'm not sure why you want to use RANK instead of what you have, but here's another way to get what you want.
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim i As Long
Dim vaNums As Variant, vaDenoms As Variant, aDivs() As Double
Dim wf As WorksheetFunction
Dim lSmall As Long
Dim rRow As Range
Dim rStart As Range
Const lCOLS As Long = 10
Const lMARKCNT As Long = 3
If Not Intersect(Target.Cells(1), Me.Range("D3", Me.Range("D3").End(xlDown))) Is Nothing Then
Set wf = Application.WorksheetFunction ' this just makes our code easier to read
'If these ever change, you only have to change them in one place
Set rRow = Target.Cells(1).Offset(0, 1).Resize(1, lCOLS)
Set rStart = Me.Cells(1, 5)
'Clear existing colors
rStart.Resize(1, lCOLS).Interior.ColorIndex = xlNone
'Read the current line and the 2nd line into arrays
'This shortcut creates two-dimensional arrays
vaNums = rRow.Value
vaDenoms = rStart.Offset(1, 0).Resize(1, lCOLS).Value
'Do the division and store it in aDivs()
ReDim aDivs(LBound(vaNums, 2) To UBound(vaNums, 2))
For i = LBound(vaNums, 2) To UBound(vaNums, 2)
aDivs(i) = vaNums(1, i) / vaDenoms(1, i) + (i / 10000)
Next i
'Find the nth smallest value and gets its position with MATCH
'Then use that position to color the cell
For i = 1 To 3
lSmall = wf.Match(wf.Small(aDivs, i), aDivs, False)
rStart.Offset(0, lSmall - 1).Interior.Color = vbYellow
Next i
End If
End Sub

Divide a string in a single cell into several cells

I have data that I need to split into individual points. My macro charts the data, as a scatter plot, with: Column A as the title of the chart, Column B as the X axis, and Columns C and D as the Y axis. What I need is for when the Product ID has more than 1 number listed to split the numbers out into their own rows and keep the columns B, C, and D the same for each row created form the original. So for row 167, I would want 3 rows (001,002,003) each with packaging, 200, and 100, in B, C, and D respectively. I am not sure where to begin. I tried to build a macro but, I immediately got tripped up when I tried to record a "Find" Formula to run on the data. Any help would be greatly appreciated.
Column A: 001, 002, 003 // Column B:packaging // Column C:200 // Column D:100
Sorry I couldn't post a screenshot of my data, the forum won't let me. If you have any questions please let me know, I will be sure to check in frequently.
Thanks in advance.
I worte this VERY quickly and without much care for efficiency, but this should do the trick:
Sub SplitUpVals()
Dim i As Long
Dim ValsToCopy As Range
Dim MaxRows As Long
Dim ValToSplit() As String
Dim CurrentVal As Variant
MaxRows = Range("A1").End(xlDown).Row
For i = 1 To 10000000
ValToSplit = Split(Cells(i, 1).Value, ",")
Set ValsToCopy = Range("B" & i & ":D" & i)
For Each CurrentVal In ValToSplit
CurrentVal = Trim(CurrentVal)
Cells(i, 1).Value = CurrentVal
Range("B" & i & ":D" & i).Value = ValsToCopy.Value
Cells(i + 1, 1).EntireRow.Insert
i = i + 1
MaxRows = MaxRows + 1
Next
Cells(i, 1).EntireRow.Delete
If i > MaxRows Then Exit For
Next i
End Sub
As a note, make sure there's no data in cells beneath your data as it might get deleted.
You will need to parse the data in column A. I would do this by splitting the string in to an array, and then iterate over the array items to add/insert additional rows where necessary.
Without seeing your worksheet, I would probably start with something like this, which will split your cell value from column A in to an array, and then you can iterate over the items in the array to manipulate the worksheet as needed.
Sub TestSplit()
Dim myString as String
Dim myArray() as String
Dim cell as Range
Dim i as Long
For each cell in Range("A2",Range("A2").End(xlDown))
myString = cell.Value
myArray = Split(myString, ",") '<-- converts the comma-delimited string in to an array
For i = lBound(myArray) to uBound(myArray)
If i >= 1 Then
'Add code to manipulate your worksheet, here
End If
Next
Next
End Sub
This is a better solution (now that I had more time :) ) - Hope this does the trick!
Sub SplitUpVals()
Dim AllVals As Variant
Dim ArrayIndex As Integer
Dim RowLooper As Integer
AllVals = Range("A1").CurrentRegion
Range("A1").CurrentRegion.Clear
RowLooper = 1
For ArrayIndex = 1 To UBound(AllVals, 1)
ValToSplit = Split(AllVals(ArrayIndex, 1), ",")
For Each CurrentVal In ValToSplit
CurrentVal = Trim(CurrentVal)
Cells(RowLooper, 1).Value = CurrentVal
Cells(RowLooper, 2).Value = AllVals(ArrayIndex, 2)
Cells(RowLooper, 3).Value = AllVals(ArrayIndex, 3)
Cells(RowLooper, 4).Value = AllVals(ArrayIndex, 4)
RowLooper = RowLooper + 1
Next
Next ArrayIndex
End Sub
Sub DivideData()
'This splits any codes combined into the same line, into their own separate lines with their own separate data
Dim a, b, txt As String, e, s, x As Long, n As Long, i As Long, ii As Long
With Range("a1").CurrentRegion
a = .Value
txt = Join$(Application.Transpose(.Columns(1).Value))
x = Len(txt) - Len(Replace(txt, ",", "")) + .Rows.Count
ReDim b(1 To x * 2, 1 To UBound(a, 2))
For i = 1 To UBound(a, 1)
For Each e In Split(a(i, 1), ",")
If e <> "" Then
For Each s In Split(e, "-")
n = n + 1
For ii = 1 To UBound(a, 2)
b(n, ii) = a(i, ii)
Next
b(n, 1) = s
Next
End If
Next
Next
With .Resize(n)
.Columns(1).NumberFormat = "#"
.Value = b
End With
End With
End Sub

Resources