{"id":12953,"date":"2026-08-28T12:52:09","date_gmt":"2026-08-28T10:52:09","guid":{"rendered":"https:\/\/www.lukaswojcik.com\/blog\/toolbox\/ga4-thresholding-modelling-checker\/"},"modified":"2026-08-28T12:52:09","modified_gmt":"2026-08-28T10:52:09","slug":"ga4-thresholding-modelling-checker","status":"publish","type":"page","link":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/ga4-thresholding-modelling-checker\/","title":{"rendered":"GA4 Thresholding and Behavioural Modelling Checker"},"content":{"rendered":"<div class=\"gtm-analyser-container\" style=\"background: var(--bg-panel, #1e1e24); padding: 25px; border-radius: 8px; border: 1px solid var(--border, #2a2a35);\">\n<p style=\"color: var(--text-secondary, #a0a0b0); margin-bottom: 20px;\">Two figures decide whether a property gets modelled data, and they pull in opposite directions: one counts daily users whose storage was granted, the other counts daily events whose storage was denied. Raising the consent rate helps the first and hurts the second, which is why modelling has an upper bound as well as a lower one. The same inputs also show how many rows of a breakdown thresholding is likely to remove.<\/p>\n<div style=\"display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; margin-bottom: 18px;\">\n<div>\n            <label for=\"tw-nutzer\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Daily users, total<\/label><br \/>\n            <input id=\"tw-nutzer\" type=\"number\" min=\"0\" step=\"100\" value=\"4000\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; box-sizing: border-box;\">\n        <\/div>\n<div>\n            <label for=\"tw-zustimmung\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Consent rate, % granted<\/label><br \/>\n            <input id=\"tw-zustimmung\" type=\"number\" min=\"0\" max=\"100\" step=\"1\" value=\"65\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; box-sizing: border-box;\">\n        <\/div>\n<div>\n            <label for=\"tw-ereignisse\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Events per user per day<\/label><br \/>\n            <input id=\"tw-ereignisse\" type=\"number\" min=\"0.1\" step=\"0.5\" value=\"12\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; box-sizing: border-box;\">\n        <\/div>\n<div>\n            <label for=\"tw-tage\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Reporting period, days<\/label><br \/>\n            <input id=\"tw-tage\" type=\"number\" min=\"1\" max=\"365\" step=\"1\" value=\"28\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; box-sizing: border-box;\">\n        <\/div>\n<div>\n            <label for=\"tw-kardinalitaet\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Distinct values in the breakdown<\/label><br \/>\n            <input id=\"tw-kardinalitaet\" type=\"number\" min=\"1\" max=\"10000\" step=\"1\" value=\"50\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; box-sizing: border-box;\">\n        <\/div>\n<div>\n            <label for=\"tw-minimum\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Assumed minimum users per row<\/label><br \/>\n            <input id=\"tw-minimum\" type=\"number\" min=\"1\" max=\"1000\" step=\"1\" value=\"50\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; box-sizing: border-box;\">\n        <\/div>\n<div>\n            <label for=\"tw-identitaet\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Reporting identity<\/label><br \/>\n            <select id=\"tw-identitaet\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; box-sizing: border-box;\"><option value=\"blended\" selected>Blended<\/option><option value=\"beobachtet\">Observed<\/option><option value=\"geraet\">Device-based<\/option><\/select>\n        <\/div>\n<div>\n            <label for=\"tw-signals\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Google Signals<\/label><br \/>\n            <select id=\"tw-signals\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; box-sizing: border-box;\"><option value=\"an\" selected>On<\/option><option value=\"aus\">Off<\/option><\/select>\n        <\/div>\n<div>\n            <label for=\"tw-demografie\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Demographic or interest dimensions<\/label><br \/>\n            <select id=\"tw-demografie\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; box-sizing: border-box;\"><option value=\"ja\">In the report<\/option><option value=\"nein\" selected>Not in the report<\/option><\/select>\n        <\/div>\n<\/p><\/div>\n<p style=\"color: var(--text-secondary, #a0a0b0); font-size: 0.82rem; margin-bottom: 16px;\">Google does not publish the row minimum that triggers thresholding, so it is an input here rather than a constant. Fifty is a commonly reported working assumption, not a documented figure &#8211; the row counts below inherit whatever value is entered.<\/p>\n<p>    <button id=\"tw-btn\" class=\"button\" style=\"background: var(--accent, #7ee787); color: var(--on-accent, #0b1114); border: none; padding: 12px 24px; border-radius: 6px; font-weight: 700; cursor: pointer;\">Calculate<\/button><\/p>\n<div id=\"tw-ausgabe\" style=\"margin-top: 22px;\"><\/div>\n<\/div>\n<p><script>\n(function () {\n    'use strict';<\/p>\n<p>    const T = {\"einleitung\":\"Two figures decide whether a property gets modelled data, and they pull in opposite directions: one counts daily users whose storage was granted, the other counts daily events whose storage was denied. Raising the consent rate helps the first and hurts the second, which is why modelling has an upper bound as well as a lower one. The same inputs also show how many rows of a breakdown thresholding is likely to remove.\",\"label_nutzer\":\"Daily users, total\",\"label_zustimmung\":\"Consent rate, % granted\",\"label_ereignisse\":\"Events per user per day\",\"label_tage\":\"Reporting period, days\",\"label_kardinalitaet\":\"Distinct values in the breakdown\",\"label_minimum\":\"Assumed minimum users per row\",\"label_identitaet\":\"Reporting identity\",\"label_signals\":\"Google Signals\",\"label_demografie\":\"Demographic or interest dimensions\",\"option_blended\":\"Blended\",\"option_beobachtet\":\"Observed\",\"option_geraet\":\"Device-based\",\"option_an\":\"On\",\"option_aus\":\"Off\",\"option_ja\":\"In the report\",\"option_nein\":\"Not in the report\",\"minimum_erklaerung\":\"Google does not publish the row minimum that triggers thresholding, so it is an input here rather than a constant. Fifty is a commonly reported working assumption, not a documented figure - the row counts below inherit whatever value is entered.\",\"knopf\":\"Calculate\",\"h_modellierung\":\"Behavioural modelling: the two conditions\",\"zeile_granted\":\"Daily users, storage granted\",\"zeile_denied\":\"Daily events, storage denied\",\"soll_tausend\":\"needs 1,000\",\"urteil_ja\":\"Both conditions met on this day\\u0027s volume.\",\"urteil_nein\":\"Not eligible: at least one condition is short.\",\"h_korridor\":\"The consent-rate corridor\",\"korridor_leer\":\"No consent rate satisfies both conditions at this volume. Modelling becomes possible from roughly {n} daily users upwards, and only then inside a corridor.\",\"zeile_untergrenze\":\"Lower bound, from the user condition\",\"zeile_obergrenze\":\"Upper bound, from the event condition\",\"zeile_mindestnutzer\":\"Daily users needed for any corridor at all\",\"h_schwellen\":\"Thresholding exposure\",\"zeile_pfad\":\"Threshold path\",\"pfad_aktiv\":\"active\",\"pfad_inaktiv\":\"not triggered by this combination\",\"zeile_nutzer_zeitraum\":\"Granted users across the period\",\"zeile_gleich\":\"Users per row, evenly split\",\"zeile_unter_zipf\":\"Rows below the minimum, long tail\",\"zeile_grenze\":\"Largest cardinality an even split still carries\",\"h_zeilen\":\"The first rows of a long tail\",\"spalte_rang\":\"Rank\",\"spalte_nutzer\":\"Users\",\"spalte_status\":\"Status\",\"status_weg\":\"removed\",\"status_da\":\"shown\",\"h_hinweise\":\"Notes\",\"hinweis_keine_daten\":\"Without a user count and an event rate there is nothing to calculate; both fields need a figure above zero.\",\"hinweis_korridor_leer\":\"The two conditions cannot both hold at this volume. Below roughly {n} daily users the lower bound sits above the upper one, so no consent rate closes the gap.\",\"hinweis_zustimmung_zu_hoch\":\"The consent rate sits above the upper bound: too few refusals remain to produce 1,000 denied events a day. This is the counter-intuitive half - more consent moves modelling further away, not closer.\",\"hinweis_zustimmung_zu_niedrig\":\"The consent rate sits below the lower bound: too few granted users remain. The second condition counts users, not events, so a high refusal rate works against modelling.\",\"hinweis_sieben_tage\":\"Both conditions have to hold on at least seven days, not once. A single strong day does not qualify a property, and eligibility can be lost again when volume falls.\",\"hinweis_schwelle_aus\":\"With this identity and Signals setting the demographic threshold path is not triggered. Small user counts can still hide rows through other paths.\",\"hinweis_hebel_identitaet\":\"Switching the reporting identity to device-based removes the Google Signals path, at the cost of the cross-device joining that identity provides.\",\"hinweis_export\":\"Rows removed by thresholding are absent, not summarised - they do not appear in an (other) row and cannot be recovered by widening the range. The BigQuery export is written before thresholding and holds them.\",\"hinweis_minimum_unveroeffentlicht\":\"The row minimum used here is the value entered above, not a documented constant. Google states that thresholds exist without naming the number, so treat the row counts as a shape rather than a prediction.\",\"dezimal\":\".\"};<\/p>\n<p>    \/\/ --- Rechnung ------------------------------------------------------------\n    \/\/\n    \/\/ Verhaltensmodellierung verlangt zwei Bedingungen zugleich, und sie zeigen\n    \/\/ in entgegengesetzte Richtungen: die eine zaehlt Nutzer MIT Einwilligung,\n    \/\/ die andere Ereignisse OHNE. Daraus wird kein einzelner Schwellwert,\n    \/\/ sondern ein Korridor - und unter einer Mindestnutzerzahl ist er leer.<\/p>\n<p>    var SCHWELLE_NUTZER = 1000;      \/\/ taegliche Nutzer mit analytics_storage = granted\n    var SCHWELLE_EREIGNISSE = 1000;  \/\/ taegliche Ereignisse mit analytics_storage = denied\n    var MINDESTTAGE = 7;<\/p>\n<p>    function zahl(wert, ersatz) {\n        var n = Number(wert);\n        return isFinite(n) ? n : ersatz;\n    }<\/p>\n<p>    \/** Harmonische Zahl, Anteilsnenner der Zipf-Verteilung. *\/\n    function harmonisch(n) {\n        var s = 0;\n        for (var i = 1; i <= n; i++) { s += 1 \/ i; }\n        return s;\n    }\n\n    function rechnen(e) {\n        var ein = e || {};\n        var nutzer = Math.max(0, zahl(ein.nutzer, 0));\n        var p = Math.min(1, Math.max(0, zahl(ein.zustimmung, 0) \/ 100));\n        var proNutzer = Math.max(0, zahl(ein.ereignisse, 0));\n        var tage = Math.max(1, Math.round(zahl(ein.tage, 1)));\n        var kardinalitaet = Math.max(1, Math.round(zahl(ein.kardinalitaet, 1)));\n        var minimum = Math.max(1, zahl(ein.minimum, 1));\n        var identitaet = ein.identitaet || 'blended';\n        var signals = ein.signals !== 'aus';\n        var demografie = ein.demografie === 'ja';\n\n        \/\/ --- Verhaltensmodellierung ---\n        var nutzerGranted = nutzer * p;\n        var nutzerDenied = nutzer * (1 - p);\n        var ereignisseDenied = nutzerDenied * proNutzer;\n\n        var bedingungNutzer = nutzerGranted >= SCHWELLE_NUTZER;\n        var bedingungEreignisse = ereignisseDenied >= SCHWELLE_EREIGNISSE;<\/p>\n<p>        \/\/ Korridor der Zustimmungsrate, in dem beide Bedingungen zugleich gelten.\n        var pMin = nutzer > 0 ? SCHWELLE_NUTZER \/ nutzer : Infinity;\n        var pMax = (nutzer > 0 && proNutzer > 0)\n            ? 1 - SCHWELLE_EREIGNISSE \/ (nutzer * proNutzer)\n            : -Infinity;\n        var korridorLeer = !(pMin <= pMax) || pMin > 1 || pMax < 0;\n\n        \/\/ Kleinste Nutzerzahl, bei der ueberhaupt eine Rate beide Bedingungen traegt.\n        var nutzerMinimum = proNutzer > 0\n            ? SCHWELLE_NUTZER * (1 + 1 \/ proNutzer)\n            : Infinity;<\/p>\n<p>        var modellierung = bedingungNutzer && bedingungEreignisse;<\/p>\n<p>        \/\/ --- Schwellenwerte im Bericht ---\n        \/\/ Der demografische Pfad haengt an Google Signals und der Identitaet;\n        \/\/ geraetebasiert liest keine Signals-Daten und loest ihn daher nicht aus.\n        var signalsPfad = signals && identitaet !== 'geraet';\n        var schwellenAktiv = signalsPfad || demografie;<\/p>\n<p>        var nutzerZeitraum = nutzerGranted * tage;\n        var gleichJeZeile = nutzerZeitraum \/ kardinalitaet;<\/p>\n<p>        \/\/ Langer Schwanz nach Zipf: Anteil der Zeile r ist (1\/r) durch H(n).\n        var H = harmonisch(kardinalitaet);\n        var zeilen = [];\n        var unterMinimumZipf = 0;\n        for (var r = 1; r <= kardinalitaet; r++) {\n            var n = nutzerZeitraum * (1 \/ r) \/ H;\n            if (n < minimum) { unterMinimumZipf++; }\n            if (zeilen.length < 8) { zeilen.push({ rang: r, nutzer: n, unten: n < minimum }); }\n        }\n        var unterMinimumGleich = gleichJeZeile < minimum ? kardinalitaet : 0;\n\n        \/\/ Groesste Kardinalitaet, bei der die Gleichverteilung noch traegt.\n        var kardinalitaetGrenze = Math.floor(nutzerZeitraum \/ minimum);\n\n        var hinweise = [];\n        if (nutzer <= 0 || proNutzer <= 0) { hinweise.push('keine_daten'); }\n        if (korridorLeer &#038;&#038; nutzer > 0 && proNutzer > 0) { hinweise.push('korridor_leer'); }\n        if (!korridorLeer && p > pMax) { hinweise.push('zustimmung_zu_hoch'); }\n        if (!korridorLeer && p < pMin) { hinweise.push('zustimmung_zu_niedrig'); }\n        if (modellierung) { hinweise.push('sieben_tage'); }\n        if (!schwellenAktiv) { hinweise.push('schwelle_aus'); }\n        if (schwellenAktiv &#038;&#038; signalsPfad &#038;&#038; !demografie) { hinweise.push('hebel_identitaet'); }\n        if (unterMinimumZipf > 0) { hinweise.push('export'); }\n        hinweise.push('minimum_unveroeffentlicht');<\/p>\n<p>        return {\n            nutzerGranted: nutzerGranted,\n            nutzerDenied: nutzerDenied,\n            ereignisseDenied: ereignisseDenied,\n            bedingungNutzer: bedingungNutzer,\n            bedingungEreignisse: bedingungEreignisse,\n            modellierung: modellierung,\n            pMin: pMin,\n            pMax: pMax,\n            korridorLeer: korridorLeer,\n            nutzerMinimum: nutzerMinimum,\n            signalsPfad: signalsPfad,\n            schwellenAktiv: schwellenAktiv,\n            nutzerZeitraum: nutzerZeitraum,\n            gleichJeZeile: gleichJeZeile,\n            unterMinimumGleich: unterMinimumGleich,\n            unterMinimumZipf: unterMinimumZipf,\n            kardinalitaet: kardinalitaet,\n            kardinalitaetGrenze: kardinalitaetGrenze,\n            zeilen: zeilen,\n            hinweise: hinweise\n        };\n    }<\/p>\n<p>    \/\/ --- Pruefpunkt ----------------------------------------------------------\n    window.LW_TEST = window.LW_TEST || {};\n    window.LW_TEST.TW = {\n        rechnen: rechnen,\n        harmonisch: harmonisch,\n        T: T,\n        SCHWELLE_NUTZER: SCHWELLE_NUTZER,\n        SCHWELLE_EREIGNISSE: SCHWELLE_EREIGNISSE,\n        MINDESTTAGE: MINDESTTAGE\n    };<\/p>\n<p>    \/\/ --- Oberflaeche ---------------------------------------------------------<\/p>\n<p>    var btn = document.getElementById('tw-btn');\n    var out = document.getElementById('tw-ausgabe');\n    if (!btn || !out) { return; }<\/p>\n<p>    function el(tag, stil, text) {\n        var e = document.createElement(tag);\n        if (stil) { e.setAttribute('style', stil); }\n        if (text !== undefined) { e.textContent = text; }\n        return e;\n    }\n    function wert(id) { var e = document.getElementById(id); return e ? e.value : ''; }\n    function komma(n, stellen) {\n        if (!isFinite(n)) { return '-'; }\n        return Number(n).toFixed(stellen === undefined ? 0 : stellen).replace('.', T.dezimal);\n    }\n    function prozent(anteil) {\n        if (!isFinite(anteil)) { return '-'; }\n        return komma(Math.min(100, Math.max(0, anteil * 100)), 1) + ' %';\n    }<\/p>\n<p>    var UEBERSCHRIFT = 'font-family: \"Nunito Sans\", sans-serif; font-weight: 700; color: var(--text-primary, #e8e8ee); font-size: 0.92rem; margin: 18px 0 8px;';\n    var ZELLE = 'padding: 5px 12px 5px 0; color: var(--text-secondary, #a0a0b0); font-size: 0.87rem;';\n    var ZELLE_WERT = 'padding: 5px 12px 5px 0; color: var(--text-primary, #e8e8ee); font-size: 0.87rem; font-family: monospace;';\n    var GUT = ' color: #7ee787;';\n    var SCHLECHT = ' color: #ffa94d;';<\/p>\n<p>    btn.addEventListener('click', function () {\n        var erg = rechnen({\n            nutzer: wert('tw-nutzer'),\n            zustimmung: wert('tw-zustimmung'),\n            ereignisse: wert('tw-ereignisse'),\n            tage: wert('tw-tage'),\n            kardinalitaet: wert('tw-kardinalitaet'),\n            minimum: wert('tw-minimum'),\n            identitaet: wert('tw-identitaet'),\n            signals: wert('tw-signals'),\n            demografie: wert('tw-demografie')\n        });<\/p>\n<p>        out.innerHTML = '';<\/p>\n<p>        \/\/ --- Verhaltensmodellierung ---\n        out.appendChild(el('div', UEBERSCHRIFT, T.h_modellierung));\n        var m = el('div', 'display: grid; grid-template-columns: auto auto auto; gap: 0 18px; align-items: baseline;');\n        function mzeile(name, ist, soll, ok) {\n            m.appendChild(el('div', ZELLE, name));\n            m.appendChild(el('div', ZELLE_WERT + (ok ? GUT : SCHLECHT), ist));\n            m.appendChild(el('div', ZELLE, soll));\n        }\n        mzeile(T.zeile_granted, komma(erg.nutzerGranted), T.soll_tausend, erg.bedingungNutzer);\n        mzeile(T.zeile_denied, komma(erg.ereignisseDenied), T.soll_tausend, erg.bedingungEreignisse);\n        out.appendChild(m);<\/p>\n<p>        out.appendChild(el('div', 'margin: 10px 0 0; font-size: 0.9rem; font-weight: 700;' + (erg.modellierung ? GUT : SCHLECHT),\n            erg.modellierung ? T.urteil_ja : T.urteil_nein));<\/p>\n<p>        \/\/ --- Korridor ---\n        out.appendChild(el('div', UEBERSCHRIFT, T.h_korridor));\n        if (erg.korridorLeer) {\n            out.appendChild(el('p', ZELLE + ' margin: 0;', T.korridor_leer.replace('{n}', komma(erg.nutzerMinimum))));\n        } else {\n            var k = el('div', 'display: grid; grid-template-columns: auto auto; gap: 0 18px; align-items: baseline;');\n            k.appendChild(el('div', ZELLE, T.zeile_untergrenze));\n            k.appendChild(el('div', ZELLE_WERT, prozent(erg.pMin)));\n            k.appendChild(el('div', ZELLE, T.zeile_obergrenze));\n            k.appendChild(el('div', ZELLE_WERT, prozent(erg.pMax)));\n            k.appendChild(el('div', ZELLE, T.zeile_mindestnutzer));\n            k.appendChild(el('div', ZELLE_WERT, komma(erg.nutzerMinimum)));\n            out.appendChild(k);\n        }<\/p>\n<p>        \/\/ --- Schwellenwerte ---\n        out.appendChild(el('div', UEBERSCHRIFT, T.h_schwellen));\n        var s = el('div', 'display: grid; grid-template-columns: auto auto; gap: 0 18px; align-items: baseline;');\n        s.appendChild(el('div', ZELLE, T.zeile_pfad));\n        s.appendChild(el('div', ZELLE_WERT + (erg.schwellenAktiv ? SCHLECHT : GUT), erg.schwellenAktiv ? T.pfad_aktiv : T.pfad_inaktiv));\n        s.appendChild(el('div', ZELLE, T.zeile_nutzer_zeitraum));\n        s.appendChild(el('div', ZELLE_WERT, komma(erg.nutzerZeitraum)));\n        s.appendChild(el('div', ZELLE, T.zeile_gleich));\n        s.appendChild(el('div', ZELLE_WERT + (erg.gleichJeZeile < minimumAnzeige() ? SCHLECHT : ''), komma(erg.gleichJeZeile, 1)));\n        s.appendChild(el('div', ZELLE, T.zeile_unter_zipf));\n        s.appendChild(el('div', ZELLE_WERT + (erg.unterMinimumZipf > 0 ? SCHLECHT : GUT), komma(erg.unterMinimumZipf) + ' \/ ' + komma(erg.kardinalitaet)));\n        s.appendChild(el('div', ZELLE, T.zeile_grenze));\n        s.appendChild(el('div', ZELLE_WERT, komma(erg.kardinalitaetGrenze)));\n        out.appendChild(s);<\/p>\n<p>        \/\/ --- Zeilen im langen Schwanz ---\n        out.appendChild(el('div', UEBERSCHRIFT, T.h_zeilen));\n        var z = el('div', 'display: grid; grid-template-columns: auto auto auto; gap: 0 18px; align-items: baseline;');\n        z.appendChild(el('div', ZELLE + ' font-weight: 700; color: var(--text-primary, #e8e8ee);', T.spalte_rang));\n        z.appendChild(el('div', ZELLE + ' font-weight: 700; color: var(--text-primary, #e8e8ee);', T.spalte_nutzer));\n        z.appendChild(el('div', ZELLE + ' font-weight: 700; color: var(--text-primary, #e8e8ee);', T.spalte_status));\n        erg.zeilen.forEach(function (r) {\n            z.appendChild(el('div', ZELLE_WERT, komma(r.rang)));\n            z.appendChild(el('div', ZELLE_WERT + (r.unten ? SCHLECHT : ''), komma(r.nutzer, 1)));\n            z.appendChild(el('div', ZELLE + (r.unten ? SCHLECHT : GUT), r.unten ? T.status_weg : T.status_da));\n        });\n        out.appendChild(z);<\/p>\n<p>        if (erg.hinweise.length) {\n            out.appendChild(el('div', UEBERSCHRIFT, T.h_hinweise));\n            var liste = el('ul', 'margin: 0 0 0 18px; padding: 0; color: var(--text-secondary, #a0a0b0); font-size: 0.87rem; line-height: 1.65;');\n            erg.hinweise.forEach(function (h) {\n                var text = T['hinweis_' + h];\n                if (text) { liste.appendChild(el('li', '', text.replace('{n}', komma(erg.nutzerMinimum)))); }\n            });\n            out.appendChild(liste);\n        }\n    });<\/p>\n<p>    \/** Angenommenes Zeilenminimum aus dem Feld, fuer die Einfaerbung. *\/\n    function minimumAnzeige() {\n        return Math.max(1, zahl(wert('tw-minimum'), 1));\n    }\n})();\n<\/script><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Behavioural modelling needs granted users and denied events at the same time, so it lives inside a consent-rate corridor rather than above a single threshold. This calculates that corridor, and how much of a report&#8217;s long tail thresholding removes.<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":38,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"template-tool-base.php","meta":{"footnotes":""},"tags":[91041,91297,91258],"class_list":["post-12953","page","type-page","status-publish","hentry","tag-consent-mode-v2","tag-data-quality","tag-google-analytics-4"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>GA4 Thresholding and Behavioural Modelling Checker - Lukas Wojcik - Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/ga4-thresholding-modelling-checker\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"GA4 Thresholding and Behavioural Modelling Checker - Lukas Wojcik - Blog\" \/>\n<meta property=\"og:description\" content=\"Behavioural modelling needs granted users and denied events at the same time, so it lives inside a consent-rate corridor rather than above a single threshold. This calculates that corridor, and how much of a report&#039;s long tail thresholding removes.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/ga4-thresholding-modelling-checker\/\" \/>\n<meta property=\"og:site_name\" content=\"Lukas Wojcik - Blog\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/og-default.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"1 minute\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/ga4-thresholding-modelling-checker\\\/\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/ga4-thresholding-modelling-checker\\\/\",\"name\":\"GA4 Thresholding and Behavioural Modelling Checker - Lukas Wojcik - Blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\"},\"datePublished\":\"2026-08-28T10:52:09+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/ga4-thresholding-modelling-checker\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/ga4-thresholding-modelling-checker\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/ga4-thresholding-modelling-checker\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Toolbox\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"GA4 Thresholding and Behavioural Modelling Checker\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\",\"name\":\"Lukas Wojcik - Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\",\"name\":\"luky\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"width\":424,\"height\":636,\"caption\":\"luky\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\"},\"sameAs\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"GA4 Thresholding and Behavioural Modelling Checker - Lukas Wojcik - Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/ga4-thresholding-modelling-checker\/","og_locale":"en_US","og_type":"article","og_title":"GA4 Thresholding and Behavioural Modelling Checker - Lukas Wojcik - Blog","og_description":"Behavioural modelling needs granted users and denied events at the same time, so it lives inside a consent-rate corridor rather than above a single threshold. This calculates that corridor, and how much of a report's long tail thresholding removes.","og_url":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/ga4-thresholding-modelling-checker\/","og_site_name":"Lukas Wojcik - Blog","og_image":[{"width":1200,"height":630,"url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/og-default.jpg","type":"image\/jpeg"}],"twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"1 minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/ga4-thresholding-modelling-checker\/","url":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/ga4-thresholding-modelling-checker\/","name":"GA4 Thresholding and Behavioural Modelling Checker - Lukas Wojcik - Blog","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#website"},"datePublished":"2026-08-28T10:52:09+00:00","breadcrumb":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/ga4-thresholding-modelling-checker\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/ga4-thresholding-modelling-checker\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/ga4-thresholding-modelling-checker\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.lukaswojcik.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Toolbox","item":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/"},{"@type":"ListItem","position":3,"name":"GA4 Thresholding and Behavioural Modelling Checker"}]},{"@type":"WebSite","@id":"https:\/\/www.lukaswojcik.com\/blog\/#website","url":"https:\/\/www.lukaswojcik.com\/blog\/","name":"Lukas Wojcik - Blog","description":"","publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.lukaswojcik.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9","name":"luky","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","width":424,"height":636,"caption":"luky"},"logo":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg"},"sameAs":["https:\/\/www.lukaswojcik.com\/blog"]}]}},"_links":{"self":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/12953","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/comments?post=12953"}],"version-history":[{"count":0,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/12953\/revisions"}],"up":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/38"}],"wp:attachment":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media?parent=12953"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/tags?post=12953"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}