Acquisition and traffic quality

Using IP clusters to investigate suspicious ad traffic

Which IPs behave like automated or pooled traffic, judged by how many geos, publishers, domains and user agents they touch?

Short answer

Trust: Inferred

Profile each IP by how widely it spreads (geos, publishers, domains, user agents), group similar IPs, then check which group carries your traffic and spend. This project stops halfway: it stores a cluster label per IP but never joins it to impressions, and cluster numbers change meaning between runs, so a label alone says nothing about risk.

Decision it supports: Which IP groups to exclude or review before paying publishers.

What to measure

IPs per cluster

Trust: Source-defined

IP rows, grouped by class.

Defined as ip_model_2_clusters.count

COUNT(*)Trust: Translated

Show the SQL: IPs per cluster
MySQL SQLTrust: Translated
COUNT(*)

Translated to MySQL SQL from ip_model_2_clusters.view.lookml L23-25. Not run against a warehouse.

Show the original LookML: ip_model_2_clusters.view.lookml L23-25
advertising_marketing_small/ip_model_2_clusters.view.lookmlL23-25
  - dimension: rel_uas    type: number    sql: ${TABLE}.rel_uas  - measure: count    type: count    detail: detail*  sets:    detail: null

advertising-marketing-small, ip_model_2_clusters.view.lookml, lines 23-25

IPs per label

Trust: Source-defined

IP rows in the label-only view.

Defined as ip_2_cluster.count

COUNT(*)Trust: Translated

Show the SQL: IPs per label
MySQL SQLTrust: Translated
COUNT(*)

Translated to MySQL SQL from ip_2_cluster.view.lookml L8-10. Not run against a warehouse.

Show the original LookML: ip_2_cluster.view.lookml L8-10
advertising_marketing_small/ip_2_cluster.view.lookmlL8-10
    sql: ${TABLE}.class  - dimension: ip    sql: ${TABLE}.ip  - measure: count    type: count    detail: detail*  sets:    detail: null

advertising-marketing-small, ip_2_cluster.view.lookml, lines 8-10

Impressions per cluster

Trust: Missing

Needs a join from cluster labels to impressions on ip. The project has none.

Which cluster is risky

Trust: Inferred

Not recorded. It has to be read from the average features per class.

The query

IPs per cluster

Trust: Suggested

How many IPs fall in each class of the two-cluster run. It is as far as the project's own fields go; the profile and the traffic per cluster need the suggested SQL below.

Show the SQL: IPs per cluster
MySQL SQLTrust: Suggested
SELECT  ip_model_2_clusters.class AS class,  COUNT(*) AS countFROM ip_model_2_clustersGROUP BY 1ORDER BY class
  • ip_model_2_clusters declares no sql_table_name, so it reads a table named ip_model_2_clusters.

Suggested query. Fields translated to MySQL SQL from odd_browser_and_os_analysis.model.lookml L10 and ip_model_2_clusters.view.lookml L3-5, L23-25. Not run against a warehouse.

The data behind it

Trust: Inferred
Entities
IP, cluster run
Grain
One row per IP per clustering run.
Time
None. Runs are not dated.
Key fields
ip_model_2_clusters.ipip_model_2_clusters.classip_model_2_clusters.geosip_model_2_clusters.pidsip_model_2_clusters.rel_uas

What can change the answer 3

Caution: Labels are not joined to traffic

Trust: Source-defined

No explore joins a cluster view to impressions, clicks or conversions.

What goes wrong: Cluster-level traffic or revenue is built by hand outside Looker, with no grain check.

Read the guide: CTR from separate tables

Source: odd_browser_and_os_analysis.model.lookml L8-12
advertising_marketing_small/odd_browser_and_os_analysis.model.lookmlL8-12
- explore: click_interactions_by_ip_for_useable_campaigns- explore: conversions_by_ip_for_useable_campaigns- explore: geo_pid_facts- explore: ip_2_cluster- explore: ip_3_cluster- explore: ip_model_2_clusters- explore: ip_model_3_clusters- explore: other_browser_os_impressions- explore: useable_campaign_ids

advertising-marketing-small, odd_browser_and_os_analysis.model.lookml, lines 8-12

Caution: Class ids change between runs

Trust: Inferred

Cluster numbers are arbitrary per run. Class 1 in the 3-cluster view has no relation to class 1 in the 2-cluster view.

Show the SQL: class
MySQL SQLTrust: Translated
ip_3_cluster.class

Translated to MySQL SQL from ip_3_cluster.view.lookml L3-5. Not run against a warehouse.

Show the original LookML: ip_3_cluster.view.lookml L3-5
advertising_marketing_small/ip_3_cluster.view.lookmlL3-5
- view: ip_3_cluster  fields:  - dimension: class    type: int    sql: ${TABLE}.class  - dimension: ip    sql: ${TABLE}.ip  - measure: count

advertising-marketing-small, ip_3_cluster.view.lookml, lines 3-5

Note: IP is an untyped string

Trust: Inferred

Every view stores ip as a string. Formatting differences or IPv6 forms can silently break a future join.

Show the SQL: ip
MySQL SQLTrust: Translated
other_browser_os_impressions.ip

Translated to MySQL SQL from other_browser_os_impressions.view.lookml L27-28. Not run against a warehouse.

Show the original LookML: other_browser_os_impressions.view.lookml L27-28
advertising_marketing_small/other_browser_os_impressions.view.lookmlL27-28
  - dimension: domain_id    type: int    sql: ${TABLE}.domain_id  - dimension: ip    sql: ${TABLE}.ip  - dimension: new_subid    sql: ${TABLE}.new_subid  - dimension: os

advertising-marketing-small, other_browser_os_impressions.view.lookml, lines 27-28

How the real implementation does it

ip cluster views

Trust: Source-defined

The model view carries the features per IP next to its class.

Source: ip_model_2_clusters.view.lookml L1-22
advertising_marketing_small/ip_model_2_clusters.view.lookmlL1-22
- view: ip_model_2_clusters  fields:  - dimension: class    type: int    sql: ${TABLE}.class  - dimension: geos    type: int    sql: ${TABLE}.geos  - dimension: ip    sql: ${TABLE}.ip  - dimension: pids    type: int    sql: ${TABLE}.pids  - dimension: rel_domains    type: number    sql: ${TABLE}.rel_domains  - dimension: rel_req    type: number    sql: ${TABLE}.rel_req  - dimension: rel_uas    type: number    sql: ${TABLE}.rel_uas  - measure: count    type: count    detail: detail*

advertising-marketing-small, ip_model_2_clusters.view.lookml, lines 1-22

ip cluster views

Trust: Source-defined

The label view keeps only IP and class, one per run.

Source: ip_2_cluster.view.lookml L1-10
advertising_marketing_small/ip_2_cluster.view.lookmlL1-10
- view: ip_2_cluster  fields:  - dimension: class    type: int    sql: ${TABLE}.class  - dimension: ip    sql: ${TABLE}.ip  - measure: count    type: count    detail: detail*  sets:    detail: null

advertising-marketing-small, ip_2_cluster.view.lookml, lines 1-10

Impressions carry the same ip string, so a join is possible in principle.

Show the SQL: the impression ip
MySQL SQLTrust: Translated
other_browser_os_impressions.ip

Translated to MySQL SQL from other_browser_os_impressions.view.lookml L27-28. Not run against a warehouse.

Show the original LookML: other_browser_os_impressions.view.lookml L27-28
advertising_marketing_small/other_browser_os_impressions.view.lookmlL27-28
  - dimension: domain_id    type: int    sql: ${TABLE}.domain_id  - dimension: ip    sql: ${TABLE}.ip  - dimension: new_subid    sql: ${TABLE}.new_subid  - dimension: os

advertising-marketing-small, other_browser_os_impressions.view.lookml, lines 27-28

Other ways to measure it

Profile each cluster

Trust: Suggested

Average the features per class so you can tell which cluster spreads across many geos, publishers and user agents.

Show the suggested SQL: cluster profile
Suggested, not from source
SELECT  class,  COUNT(*) AS ips,  AVG(geos) AS avg_geos,  AVG(pids) AS avg_publishers,  AVG(rel_domains) AS avg_rel_domains,  AVG(rel_uas) AS avg_rel_user_agentsFROM ip_model_2_clustersGROUP BY class

Join labels to the traffic

Trust: Suggested

Attach one clustering run to impressions so traffic and bids can be grouped by cluster. It assumes one row per IP in that run; in LookML the same join is a many_to_one on ip.

Show the suggested SQL: impressions and bids per cluster
Suggested, not from source
SELECT  c.class,  COUNT(*) AS impressions,  SUM(i.bid_amount) AS bidsFROM other_browser_os_impressions AS iJOIN ip_model_2_clusters AS c ON c.ip = i.ipGROUP BY c.class

Name the clusters

Trust: Suggested

Store a label per class and run, such as suspicious and normal, in a small mapping table, and join it to the label views.