Attribution modeling with the paths table

The *_paths tables give you insight into the interactions users have with the media you manage using Display and Video 360. These interactions (also known as touchpoints) are stitched together into event paths, chronicling the journey that a group of users takes through your conversion funnel. The data in the *_paths tables uses the same data that's available to you via Data Transfer, but comes to you in preassembled paths. Event paths are available for both converting and non-converting users and can include multiple conversions.

Just as with all user data in Ads Data Hub, data from the *_paths tables is subject to aggregation requirements.

Technical details

Each row of paths contains a single customer journey over a 30-day window. This data is updated daily to cover the last 30 days. Event paths are assembled using cookies, and therefore are limited to a single device. In certain situations, such as when users have opted out of ads personalization, user IDs will be set to 0. Normally, these users will still be separated by row, so 2 distinct conversion paths keyed to a user ID of 0 will have distinct rows. However, for certain types of analysis — such as grouping by user_id = 0 — multiple conversion paths will be merged into one, which could create discrepancies in your data. Learn more about zeroed user IDs

Each row of the *_paths.events column contains an array of structs where each struct is a single event in the user's path. The structs that make up the path are ordered by timestamp, with the first event in the array being the oldest.

Sample queries

The following queries measure the impact of placements towards a particular user segment converting. The queries attribute credit using 3 different attribution models:

  • First-touch. (All credit goes to the first touchpoint.)
  • Last-touch. (all credit goes to the last touchpoint.)
  • Linear. (Credit is divided evenly among the touchpoints.)

Sample data

Row user_id *_paths.events.event_time *_paths.events.event_type *_paths.events.placement_id
1 1 1563384139 FLOODLIGHT null
1563384129 CLICK 11
1563384119 VIEW 22
2 2 1563384139 FLOODLIGHT null
1563384129 VIEW 11
1563384119 FLOODLIGHT null
1563384109 VIEW 11

Sample queries

First-touch

/* Substitute *_paths for the specific paths table that you want to query. */
SELECT
  (
  SELECT
    attributed_event_metadata.placement_id
  FROM (
    SELECT
      AS STRUCT attributed_event.placement_id,
      ROW_NUMBER() OVER(ORDER BY attributed_event.event_time ASC) AS rank
    FROM
      UNNEST(t.*_paths.events) AS attributed_event
    WHERE
      attributed_event.event_type != "FLOODLIGHT"
      AND attributed_event.event_time < conversion_event.event_time
      AND attributed_event.event_time > (
      SELECT
        IFNULL( (
          SELECT
            MAX(prev_conversion_event.event_time) AS event_time
          FROM
            UNNEST(t.*_paths.events) AS prev_conversion_event
          WHERE
            prev_conversion_event.event_type = "FLOODLIGHT"
            AND prev_conversion_event.event_time < conversion_event.event_time),
          0)) ) AS attributed_event_metadata
  WHERE
    attributed_event_metadata.rank = 1) AS placement_id,
  COUNT(*) AS credit
FROM
  adh.*_paths AS t,
  UNNEST(*_paths.events) AS conversion_event
WHERE
  conversion_event.event_type = "FLOODLIGHT"
GROUP BY
  placement_id
HAVING
  placement_id IS NOT NULL
ORDER BY
  credit DESC

Last-touch

/* Substitute *_paths for the specific paths table that you want to query. */
SELECT
  (
  SELECT
    attributed_event_metadata.placement_id
  FROM (
    SELECT
      AS STRUCT attributed_event.placement_id,
      ROW_NUMBER() OVER(ORDER BY attributed_event.event_time DESC) AS rank
    FROM
      UNNEST(t.*_paths.events) AS attributed_event
    WHERE
      attributed_event.event_type != "FLOODLIGHT"
      AND attributed_event.event_time < conversion_event.event_time
      AND attributed_event.event_time > (
      SELECT
        IFNULL( (
          SELECT
            MAX(prev_conversion_event.event_time) AS event_time
          FROM
            UNNEST(t.*_paths.events) AS prev_conversion_event
          WHERE
            prev_conversion_event.event_type = "FLOODLIGHT"
            AND prev_conversion_event.event_time < conversion_event.event_time),
          0)) ) AS attributed_event_metadata
  WHERE
    attributed_event_metadata.rank = 1) AS placement_id,
  COUNT(*) AS credit
FROM
  adh.*_paths AS t,
  UNNEST(*_paths.events) AS conversion_event
WHERE
  conversion_event.event_type = "FLOODLIGHT"
GROUP BY
  placement_id
HAVING
  placement_id IS NOT NULL
ORDER BY
  credit DESC

Linear

/* Substitute *_paths for the specific paths table that you want to query. */
SELECT
  attributed_event_metadata.placement_id AS placement_id,
  /* Give equal credit to all attributed events */
  SUM(SAFE_DIVIDE(1, ARRAY_LENGTH(attributed_events_metadata)))
FROM (
  SELECT
    ARRAY(
    SELECT
      AS STRUCT attributed_event.placement_id,
      ROW_NUMBER() OVER(ORDER BY attributed_event.event_time DESC) AS rank
    FROM
      UNNEST(t.*_paths.events) AS attributed_event
    WHERE
      attributed_event.event_type!="FLOODLIGHT"
      AND attributed_event.event_time < conversion_event.event_time
      AND attributed_event.event_time > (
      SELECT
        MAX(prev_conversion_event.event_time) AS event_time
      FROM
        UNNEST(t.*_paths.events) AS prev_conversion_event
      WHERE
        prev_conversion_event.event_type="FLOODLIGHT"
        AND prev_conversion_event.event_time < conversion_event.event_time)) AS attributed_events_metadata
  FROM
    adh.*_paths AS t,
    UNNEST(*_paths.events) AS conversion_event
  WHERE
    conversion_event.event_type="FLOODLIGHT" ),
  UNNEST(attributed_events_metadata) AS attributed_event_metadata
GROUP BY
  1
HAVING
  placement_id IS NOT NULL
ORDER BY
  2 DESC

Results

If you were to run the query on the sample data, you would get these results out:

First-touch

row placement_id credit
1 11 2
2 22 1

Last-touch

row placement_id credit
1 11 3

Linear

row placement_id credit
1 11 2.5
2 22 0.5