79724709

Date: 2025-08-04 09:36:33
Score: 1.5
Natty:
Report link
Option Explicit

Public Sub Benford_ColumnG_ToSheet_MacFixed()
    Const DATA_SHEET_NAME As String = "2018"   ' e.g., "Data"; blank "" = ActiveSheet
    Const DATA_COL As String = "G"         ' change if your numbers are in a different column
    Const START_ROW As Long = 2            ' set to 1 if no header row

    Dim wsData As Worksheet, wsOut As Worksheet
    Dim lastRow As Long, i As Long
    Dim arr As Variant, v As Variant
    Dim counts(1 To 9) As Long
    Dim total As Long, d As Long
    Dim expected As Double, obs As Double
    Dim chisq As Double, mad As Double
    Dim chartObj As ChartObject
    Dim co As ChartObject

    On Error GoTo CleanFail
    Application.ScreenUpdating = False
    Application.EnableEvents = False

    ' Pick the data sheet
    If Len(DATA_SHEET_NAME) > 0 Then
        Set wsData = ThisWorkbook.Worksheets(DATA_SHEET_NAME)
    Else
        Set wsData = ActiveSheet
    End If

    ' Find last row with data in the chosen column
    lastRow = wsData.Cells(wsData.Rows.Count, DATA_COL).End(xlUp).Row
    If lastRow < START_ROW Then
        MsgBox "No data found in column " & DATA_COL & " at or below row " & START_ROW & ".", vbExclamation
        GoTo CleanExit
    End If

    ' Load to array (fast)
    arr = wsData.Range(DATA_COL & START_ROW & ":" & DATA_COL & lastRow).Value

    ' Count first significant digits
    total = 0
    For i = 1 To UBound(arr, 1)
        v = arr(i, 1)
        d = FirstSignificantDigitFromValue(v)  ' 0 if none/zero
        If d >= 1 And d <= 9 Then
            counts(d) = counts(d) + 1
            total = total + 1
        End If
    Next i

    If total = 0 Then
        MsgBox "No analyzable values (first digit 1–9) found in column " & DATA_COL & ".", vbExclamation
        GoTo CleanExit
    End If

    ' Create/clear output sheet
    On Error Resume Next
    Set wsOut = ThisWorkbook.Worksheets("Benford")
    On Error GoTo 0
    If wsOut Is Nothing Then
        Set wsOut = ThisWorkbook.Worksheets.Add(After:=wsData)
        wsOut.Name = "Benford"
    Else
        wsOut.Cells.Clear
    End If

    With wsOut
        ' Headers
        .Range("A1:E1").Value = Array("Digit", "Count", "% of Total", "Benford %", "Diff (Obs - Exp)")
        .Range("A1:E1").Font.Bold = True

        ' Body
        For d = 1 To 9
            .Cells(d + 1, "A").Value = d
            .Cells(d + 1, "B").Value = counts(d)
            .Cells(d + 1, "D").Value = Log(1# + 1# / d) / Log(10#) ' Benford expected %
        Next d

        .Range("A11").Value = "Total"
        .Range("B11").FormulaR1C1 = "=SUM(R[-9]C:R[-1]C)"
        .Range("C2:C10").FormulaR1C1 = "=RC[-1]/R11C2"     ' observed %
        .Range("E2:E10").FormulaR1C1 = "=RC[-2]-RC[-1]"    ' diff %

        .Range("B2:B11").NumberFormat = "0"
        .Range("C2:C10,D2:D10,E2:E10").NumberFormat = "0.000%"
        .Columns("A:E").AutoFit

        ' Chi-square
        chisq = 0#
        For d = 1 To 9
            expected = total * .Cells(d + 1, "D").Value
            chisq = chisq + ((counts(d) - expected) ^ 2) / expected
        Next d
        .Range("A13").Value = "Chi-square"
        .Range("B13").Value = chisq

        ' p-value (df=8) via worksheet formula (works on Mac/Win)
        .Range("A14").Value = "p-value (df=8)"
        .Range("B14").Formula = "=IFERROR(CHISQ.DIST.RT(B13,8),IFERROR(CHIDIST(B13,8),""n/a""))"
        .Range("B14").NumberFormat = "0.0000"

        ' MAD and class
        mad = 0#
        For d = 1 To 9
            obs = .Cells(d + 1, "C").Value
            expected = .Cells(d + 1, "D").Value
            mad = mad + Abs(obs - expected)
        Next d
        mad = mad / 9#
        .Range("A15").Value = "MAD"
        .Range("B15").Value = mad
        .Range("B15").NumberFormat = "0.0000"

        .Range("A16").Value = "MAD Class"
        .Range("B16").Formula = "=IF(B15<0.006,""Close conformity"",IF(B15<0.012,""Acceptable"",IF(B15<0.015,""Marginal"",""Nonconformity"")))"

        ' Remove any existing charts (Mac-stable)
        For Each co In .ChartObjects
            co.Delete
        Next co

        ' Add chart
        Set chartObj = .ChartObjects.Add(Left:=.Range("G2").Left, Top:=.Range("G2").Top, Width:=420, Height:=280)
        With chartObj.Chart
            .ChartType = xlColumnClustered

            Do While .SeriesCollection.Count > 0
                .SeriesCollection(1).Delete
            Loop

            With .SeriesCollection.NewSeries
                .Name = "% Observed"
                .XValues = wsOut.Range("A2:A10")  ' *** Mac-safe: explicit worksheet ***
                .Values = wsOut.Range("C2:C10")
            End With
            With .SeriesCollection.NewSeries
                .Name = "Benford %"
                .XValues = wsOut.Range("A2:A10")  ' *** Mac-safe: explicit worksheet ***
                .Values = wsOut.Range("D2:D10")
            End With

            .HasTitle = True
            .ChartTitle.Text = "Benford's Law ? First Digit (" & wsData.Name & "!" & DATA_COL & ")"
            .Axes(xlValue).TickLabels.NumberFormat = "0%"
            .Legend.Position = xlLegendPositionBottom
        End With
    End With

    MsgBox "Benford analysis complete. See the 'Benford' sheet.", vbInformation

CleanExit:
    Application.ScreenUpdating = True
    Application.EnableEvents = True
    Exit Sub

CleanFail:
    Application.ScreenUpdating = True
    Application.EnableEvents = True
    MsgBox "Error: " & Err.Description, vbExclamation
End Sub

' First significant digit helper (unchanged)
Private Function FirstSignificantDigitFromValue(ByVal v As Variant) As Integer
    Dim x As Double
    Dim s As String
    Dim i As Long, ch As Integer

    If IsNumeric(v) Then
        x = Abs(CDbl(v))
        If x = 0# Then
            FirstSignificantDigitFromValue = 0
            Exit Function
        End If
        Do While x >= 10#
            x = x / 10#
        Loop
        Do While x < 1#
            x = x * 10#
        Loop
        FirstSignificantDigitFromValue = Int(x)
        If FirstSignificantDigitFromValue < 1 Or FirstSignificantDigitFromValue > 9 Then
            FirstSignificantDigitFromValue = 0
        End If
    Else
        s = CStr(v)
        For i = 1 To Len(s)
            ch = Asc(Mid$(s, i, 1))
            If ch >= 49 And ch <= 57 Then
                FirstSignificantDigitFromValue = ch - 48
                Exit Function
            End If
        Next i
        FirstSignificantDigitFromValue = 0
    End If
End Function

the only thing you have to change is the name of the sheet you want analysed and the column that your data are everthing else is copy paste and it works on macos too
posting this because i couldnt find it anywhere else for excel vba and i want people to have it enter image description here

Reasons:
  • Blacklisted phrase (1): enter image description here
  • RegEx Blacklisted phrase (1): i want
  • Long answer (-1):
  • Has code block (-0.5):
  • Low reputation (1):
Posted by: Thanasis Papazikos