개요

Google Analytics Data API v1을 사용하면 유입경로 보고서를 생성할 수 있습니다. 유입경로 탐색 분석을 사용하면 사용자가 작업을 완료하기 위해 실행하는 단계를 시각화하고 단계별 작업 완료 또는 실패 여부를 신속하게 확인할 수 있습니다.

핵심 보고서와 함께 제공되는 기능

유입경로 보고 요청은 여러 공유 기능에 대한 핵심 보고서 요청과 동일한 시맨틱스를 갖습니다. 예를 들어 페이지로 나누기, 측정기준 필터, 사용자 속성은 유입경로 보고서에서 핵심 보고서와 동일하게 작동합니다. 이 가이드에서는 유입경로 보고 기능을 중점적으로 설명합니다. Data API v1의 Core Reporting 기능에 익숙해지려면 보고 기본사항 가이드고급 사용 사례 가이드를 참고하세요.

유입경로 보고 방법

Data API v1은 runFunnelReport 메서드에서 유입경로 보고 기능을 지원합니다. 이 메서드는 Google 애널리틱스 이벤트 데이터의 맞춤설정된 유입경로 보고서를 반환합니다.

보고 항목 선택

Data API v1의 모든 메서드는 다음과 같이 properties/GA4_PROPERTY_ID 형식의 URL 요청 경로 내에 Google 애널리틱스 4 속성 식별자를 지정해야 합니다.

  POST  https://analyticsdata.googleapis.com/v1alpha/properties/GA4_PROPERTY_ID:runFunnelReport

결과 보고서는 지정된 Google 애널리틱스 4 속성에서 수집된 Google 애널리틱스 이벤트 데이터를 기반으로 생성됩니다.

Data API 클라이언트 라이브러리 중 하나를 사용하는 경우 요청 URL 경로를 수동으로 조작할 필요가 없습니다. 대부분의 API 클라이언트는 properties/GA4_PROPERTY_ID 형식의 문자열을 예상하는 property 매개변수를 제공합니다. 클라이언트 라이브러리 사용 예는 빠른 시작 가이드를 참고하세요.

유입경로 보고서 요청

유입경로 보고서를 요청하려면 RunFunnelReportRequest 객체를 구성하면 됩니다. 먼저 다음 요청 매개변수로 시작하는 것이 좋습니다.

  • dateRanges 필드의 유효한 항목.

  • funnel 필드에 유효한 유입경로 사양이 있어야 합니다.

유입경로 사양

RunFunnelReportRequest 객체의 funnel 필드에 있는 유입경로 사양은 이 유입경로의 steps를 설명하여 측정하려는 사용자 여정을 정의합니다.

유입경로 단계에는 사용자가 유입경로의 해당 단계에 포함되기 위해 충족해야 하는 조건이 하나 이상 포함됩니다. 각 단계에 포함되는 조건은 각 단계의 filterExpression 필드에서 설명할 수 있습니다.

각 유입경로 필터 표현식은 다음 두 가지 필터의 조합입니다.

  • funnelFieldFilter는 측정기준 또는 측정항목에 대한 필터를 만듭니다.

  • funnelEventFilter는 단일 이벤트 이름의 이벤트와 일치하는 필터를 만듭니다. 선택사항인 funnelParameterFilterExpression 필드를 지정하면 단일 이벤트 이름 및 매개변수 필터 표현식과 모두 일치하는 이벤트의 하위 집합만 이 이벤트 필터와 일치합니다.

필터는 AND, OR 그룹을 사용하여 결합하거나 NOT 표현식을 사용하여 무효화할 수 있습니다.

각 유입경로 단계의 보고서 결과는 측정기준에 따라 분류되며 funnelBreakdown 필드에 지정됩니다.

유입경로 보고서의 예

Google 애널리틱스 Data API v1을 사용하여 Google 애널리틱스 UI의 유입경로 탐색 분석 템플릿에 제공된 기본 유입경로 보고서를 재현해 보겠습니다.

샘플 유입경로 보고서 UI

유입경로 단계

위의 유입경로 구성은 다음 단계로 구성됩니다.

# 단계 이름 조건
1 처음 열 때/방문 이벤트 이름은 first_open 또는 first_visit입니다.
2 자연 방문자 firstUserMedium 측정기준에 '자연'이라는 용어가 포함되어 있습니다.
3 세션 시작 이벤트 이름은 session_start입니다.
4 화면/페이지 조회 이벤트 이름은 screen_view 또는 page_view입니다.
5 구매 이벤트 이름은 purchase 또는 in_app_purchase입니다.

유입경로의 1단계(첫 번째 열기/방문)에는 웹사이트 또는 애플리케이션과 처음 상호작용한 후의 모든 사용자(즉, first_open 또는 first_visit 이벤트를 트리거한 사용자)가 포함됩니다.

이 동작을 구현하기 위해 아래 스니펫은 filterExpression 필드가 있는 FunnelStep 객체를 지정합니다. 필터 표현식 필드는 OR 그룹을 사용하여 두 개의 FunnelEventFilter 항목을 결합하여 구성된 FunnelFilterExpression 객체입니다.

  {
    "name": "Purchase",
    "filterExpression": {
      "orGroup": {
        "expressions": [
          {
            "funnelEventFilter": {
              "eventName": "first_open"
            }
          },
          {
            "funnelEventFilter": {
              "eventName": "first_visit"
            }
          }
        ]
      }
    }
  }

유입경로의 2단계 (자연 방문자)에는 첫 번째 매체에 '자연'이라는 용어가 포함된 사용자가 포함됩니다. 아래 스니펫에서 FunnelFieldFilterfieldName 필드는 필터가 firstUserMedium 측정기준과 일치하도록 지정합니다. stringFilter 필드에는 '자연 검색'이라는 용어가 포함된 측정기준의 값만 포함하기 위한 조건이 포함됩니다.

  {
    "name": "Organic visitors",
    "filterExpression": {
      "funnelFieldFilter": {
        "fieldName": "firstUserMedium",
        "stringFilter": {
          "matchType": "CONTAINS",
          "caseSensitive": false,
          "value": "organic"
        }
      }
    }
  }

나머지 유입경로 단계는 비슷한 방식으로 지정할 수 있습니다.

세부 측정기준

선택사항인 세부 측정기준 (이 예시에서는 deviceCategory)은 FunnelBreakdown 객체를 사용하여 지정할 수 있습니다.

  "funnelBreakdown": {
    "breakdownDimension": {
      "name": "deviceCategory"
    }
  }

기본적으로 세부 측정기준의 처음 5개 고유 값만 보고서에 포함됩니다. FunnelBreakdown 객체의 limit 필드를 사용하여 이 동작을 재정의할 수 있습니다.

전체 유입경로 보고서 쿼리

다음은 위에서 설명한 단계를 모두 사용하여 유입경로 보고서를 생성하는 전체 쿼리입니다.

HTTP

POST https://analyticsdata.googleapis.com/v1alpha/properties/GA4_PROPERTY_ID:runFunnelReport
{
  "dateRanges": [
    {
      "startDate": "30daysAgo",
      "endDate": "today"
    }
  ],
  "funnelBreakdown": {
    "breakdownDimension": {
      "name": "deviceCategory"
    }
  },
  "funnel": {
    "steps": [
      {
        "name": "First open/visit",
        "filterExpression": {
          "orGroup": {
            "expressions": [
              {
                "funnelEventFilter": {
                  "eventName": "first_open"
                }
              },
              {
                "funnelEventFilter": {
                  "eventName": "first_visit"
                }
              }
            ]
          }
        }
      },
      {
        "name": "Organic visitors",
        "filterExpression": {
          "funnelFieldFilter": {
            "fieldName": "firstUserMedium",
            "stringFilter": {
              "matchType": "CONTAINS",
              "caseSensitive": false,
              "value": "organic"
            }
          }
        }
      },
      {
        "name": "Session start",
        "filterExpression": {
          "funnelEventFilter": {
            "eventName": "session_start"
          }
        }
      },
      {
        "name": "Screen/Page view",
        "filterExpression": {
          "orGroup": {
            "expressions": [
              {
                "funnelEventFilter": {
                  "eventName": "screen_view"
                }
              },
              {
                "funnelEventFilter": {
                  "eventName": "page_view"
                }
              }
            ]
          }
        }
      },
      {
        "name": "Purchase",
        "filterExpression": {
          "orGroup": {
            "expressions": [
              {
                "funnelEventFilter": {
                  "eventName": "purchase"
                }
              },
              {
                "funnelEventFilter": {
                  "eventName": "in_app_purchase"
                }
              }
            ]
          }
        }
      }
    ]
  }
}

응답 신고

유입경로 보고서 API 요청의 유입경로 보고서 응답FunnelSubReport 객체로 반환되는 두 가지 주요 부분인 유입경로 시각화유입경로 표로 구성됩니다.

유입경로 시각화

유입경로 보고서 응답funnelVisualization 필드에 반환되는 유입경로 시각화에는 유입경로 보고서에 대한 대략적인 개요가 포함되어 있습니다. 이름에서 알 수 있듯이 생성된 유입경로 보고서를 빠르게 시각화하는 데 유용합니다.

유입경로 시각화 표의 각 행에는 다음 필드 중 일부 또는 전체가 포함됩니다.

  • 유입경로 단계 이름 (funnelStepName 측정기준)

  • 활성 사용자 수 (측정항목 activeUsers개)

  • 세그먼트 (segment 측정기준) Segment가 유입경로 쿼리에 지정된 경우에만 존재합니다.

  • 날짜 (date 측정기준) 쿼리에 TRENDED_FUNNEL 시각화 유형이 지정된 경우에만 존재합니다.

  • 다음 액션 측정기준 (funnelStepNextAction 측정기준) FunnelNextAction가 유입경로 쿼리에 지정된 경우에만 존재합니다.

위에서 설명한 예시 보고서의 유입경로 시각화 섹션이 Google 애널리틱스 UI에 표시되는 방식은 다음과 같습니다.

유입경로 보고서 헤더: 샘플

유입경로 표

유입경로 보고서 응답funnelTable 필드에 반환되는 유입경로 표는 보고서의 주요 부분을 나타냅니다. 테이블의 각 행에는 다음 필드 중 일부 또는 전부가 포함됩니다.

  • 유입경로 단계 이름 (funnelStepName 측정기준)

  • 세부 측정기준.

  • 활성 사용자 수 (측정항목 activeUsers개)

  • 단계 완료율 (funnelStepCompletionRate 측정항목)

  • 걸음 이탈 횟수 (측정항목 funnelStepAbandonments개)

  • 단계 이탈률 (funnelStepAbandonmentRate 측정항목)

  • 세그먼트 이름 (segment 측정기준) Segment가 유입경로 쿼리에 지정된 경우에만 존재합니다.

핵심 보고 기능과 마찬가지로 총값은 RESERVED_TOTAL를 세부 측정기준 값으로 갖는 별도의 행에 반환됩니다.

다음은 Google 애널리틱스 UI에 표시되는 유입경로 표의 예입니다. 유입경로 보고서 표: 샘플

원시 응답

아래 스니펫은 runFunnelReport 쿼리에 대한 응답으로 반환된 원시 데이터의 예를 보여줍니다.

속성에서 수집한 데이터에 따라, 위의 예시 보고서에서는 각 유입경로 단계에 포함된 활성 사용자 수를 보여주는 다음과 같은 보고서를 반환합니다.

{
  "funnelTable": {
    "dimensionHeaders": [
      {
        "name": "funnelStepName"
      },
      {
        "name": "deviceCategory"
      }
    ],
    "metricHeaders": [
      {
        "name": "activeUsers",
        "type": "TYPE_INTEGER"
      },
      {
        "name": "funnelStepCompletionRate",
        "type": "TYPE_INTEGER"
      },
      {
        "name": "funnelStepAbandonments",
        "type": "TYPE_INTEGER"
      },
      {
        "name": "funnelStepAbandonmentRate",
        "type": "TYPE_INTEGER"
      }
    ],
    "rows": [
      {
        "dimensionValues": [
          {
            "value": "1. First open/visit"
          },
          {
            "value": "RESERVED_TOTAL"
          }
        ],
        "metricValues": [
          {
            "value": "4621565"
          },
          {
            "value": "0.27780178359495106"
          },
          {
            "value": "3337686"
          },
          {
            "value": "0.72219821640504889"
          }
        ]
      },
      {
        "dimensionValues": [
          {
            "value": "1. First open/visit"
          },
          {
            "value": "desktop"
          }
        ],
        "metricValues": [
          {
            "value": "4015959"
          },
          {
            "value": "0.27425279989163237"
          },
          {
            "value": "2914571"
          },
          {
            "value": "0.72574720010836768"
          }
        ]
      },
      {
        "dimensionValues": [
          {
            "value": "1. First open/visit"
          },
          {
            "value": "mobile"
          }
        ],
        "metricValues": [
          {
            "value": "595760"
          },
          {
            "value": "0.29156035987646034"
          },
          {
            "value": "422060"
          },
          {
            "value": "0.70843964012353966"
          }
        ]
      },
      {
        "dimensionValues": [
          {
            "value": "1. First open/visit"
          },
          {
            "value": "tablet"
          }
        ],
        "metricValues": [
          {
            "value": "33638"
          },
          {
            "value": "0.205571080325822"
          },
          {
            "value": "26723"
          },
          {
            "value": "0.79442891967417806"
          }
        ]
      },

...

    ],
    "metadata": {
      "samplingMetadatas": [
        {
          "samplesReadCount": "9917254",
          "samplingSpaceSize": "1162365416"
        }
      ]
    }
  },

  "funnelVisualization": {
    "dimensionHeaders": [
      {
        "name": "funnelStepName"
      }
    ],
    "metricHeaders": [
      {
        "name": "activeUsers",
        "type": "TYPE_INTEGER"
      }
    ],
    "rows": [
      {
        "dimensionValues": [
          {
            "value": "1. First open/visit"
          }
        ],
        "metricValues": [
          {
            "value": "4621565"
          }
        ]
      },

...

    ],
    "metadata": {
      "samplingMetadatas": [
        {
          "samplesReadCount": "9917254",
          "samplingSpaceSize": "1162365416"
        }
      ]
    }
  },
  "kind": "analyticsData#runFunnelReport"
}

클라이언트 라이브러리

클라이언트 라이브러리를 설치하고 구성하는 방법에 대한 설명은 빠른 시작 가이드를 참고하세요.

다음은 유입경로 쿼리를 실행하고 응답을 출력하는 클라이언트 라이브러리를 사용하는 예입니다.

Java

import com.google.analytics.data.v1alpha.AlphaAnalyticsDataClient;
import com.google.analytics.data.v1alpha.DateRange;
import com.google.analytics.data.v1alpha.Dimension;
import com.google.analytics.data.v1alpha.DimensionHeader;
import com.google.analytics.data.v1alpha.FunnelBreakdown;
import com.google.analytics.data.v1alpha.FunnelEventFilter;
import com.google.analytics.data.v1alpha.FunnelFieldFilter;
import com.google.analytics.data.v1alpha.FunnelFilterExpression;
import com.google.analytics.data.v1alpha.FunnelFilterExpressionList;
import com.google.analytics.data.v1alpha.FunnelStep;
import com.google.analytics.data.v1alpha.FunnelSubReport;
import com.google.analytics.data.v1alpha.MetricHeader;
import com.google.analytics.data.v1alpha.Row;
import com.google.analytics.data.v1alpha.RunFunnelReportRequest;
import com.google.analytics.data.v1alpha.RunFunnelReportResponse;
import com.google.analytics.data.v1alpha.SamplingMetadata;
import com.google.analytics.data.v1alpha.StringFilter;
import com.google.analytics.data.v1alpha.StringFilter.MatchType;

/**
 * Google Analytics Data API sample application demonstrating the creation of a funnel report.
 *
 * <p>See
 * https://developers.google.com/analytics/devguides/reporting/data/v1/rest/v1alpha/properties/runFunnelReport
 * for more information.
 *
 * <p>Before you start the application, please review the comments starting with "TODO(developer)"
 * and update the code to use correct values.
 *
 * <p>To run this sample using Maven:
 *
 * <pre>{@code
 * cd google-analytics-data
 * mvn compile exec:java -Dexec.mainClass="com.google.analytics.data.samples.RunFunnelReportSample"
 * }</pre>
 */
public class RunFunnelReportSample {

  public static void main(String... args) throws Exception {
    /**
     * TODO(developer): Replace this variable with your Google Analytics 4 property ID before
     * running the sample.
     */
    String propertyId = "YOUR-GA4-PROPERTY-ID";
    sampleRunFunnelReport(propertyId);
  }

  /**
   * Runs a funnel query to build a report with 5 funnel steps.
   *
   * <ol>
   *   <li>First open/visit (event name is `first_open` or `first_visit`).
   *   <li>Organic visitors (`firstUserMedium` dimension contains the term "organic").
   *   <li>Session start (event name is `session_start`).
   *   <li>Screen/Page view (event name is `screen_view` or `page_view`).
   *   <li>Purchase (event name is `purchase` or `in_app_purchase`).
   * </ol>
   *
   * The report configuration reproduces the default funnel report provided in the Funnel
   * Exploration template of the Google Analytics UI. See more at
   * https://support.google.com/analytics/answer/9327974
   */
  static void sampleRunFunnelReport(String propertyId) throws Exception {

    // Using a default constructor instructs the client to use the credentials
    // specified in GOOGLE_APPLICATION_CREDENTIALS environment variable.
    try (AlphaAnalyticsDataClient analyticsData = AlphaAnalyticsDataClient.create()) {
      RunFunnelReportRequest.Builder requestBuilder =
          RunFunnelReportRequest.newBuilder()
              .setProperty("properties/" + propertyId)
              .addDateRanges(DateRange.newBuilder().setStartDate("30daysAgo").setEndDate("today"))
              .setFunnelBreakdown(
                  FunnelBreakdown.newBuilder()
                      .setBreakdownDimension(Dimension.newBuilder().setName("deviceCategory")));

      // Adds each step of the funnel.
      requestBuilder
          .getFunnelBuilder()
          .addSteps(
              FunnelStep.newBuilder()
                  .setName("First open/visit")
                  .setFilterExpression(
                      FunnelFilterExpression.newBuilder()
                          .setOrGroup(
                              FunnelFilterExpressionList.newBuilder()
                                  .addExpressions(
                                      FunnelFilterExpression.newBuilder()
                                          .setFunnelEventFilter(
                                              FunnelEventFilter.newBuilder()
                                                  .setEventName("first_open")))
                                  .addExpressions(
                                      FunnelFilterExpression.newBuilder()
                                          .setFunnelEventFilter(
                                              FunnelEventFilter.newBuilder()
                                                  .setEventName("first_visit"))))));
      requestBuilder
          .getFunnelBuilder()
          .addSteps(
              FunnelStep.newBuilder()
                  .setName("Organic visitors")
                  .setFilterExpression(
                      FunnelFilterExpression.newBuilder()
                          .setFunnelFieldFilter(
                              FunnelFieldFilter.newBuilder()
                                  .setFieldName("firstUserMedium")
                                  .setStringFilter(
                                      StringFilter.newBuilder()
                                          .setMatchType(MatchType.CONTAINS)
                                          .setCaseSensitive(false)
                                          .setValue("organic")))));
      requestBuilder
          .getFunnelBuilder()
          .addSteps(
              FunnelStep.newBuilder()
                  .setName("Session start")
                  .setFilterExpression(
                      FunnelFilterExpression.newBuilder()
                          .setFunnelEventFilter(
                              FunnelEventFilter.newBuilder().setEventName("session_start"))));

      requestBuilder
          .getFunnelBuilder()
          .addSteps(
              FunnelStep.newBuilder()
                  .setName("Screen/Page view")
                  .setFilterExpression(
                      FunnelFilterExpression.newBuilder()
                          .setOrGroup(
                              FunnelFilterExpressionList.newBuilder()
                                  .addExpressions(
                                      FunnelFilterExpression.newBuilder()
                                          .setFunnelEventFilter(
                                              FunnelEventFilter.newBuilder()
                                                  .setEventName("screen_view")))
                                  .addExpressions(
                                      FunnelFilterExpression.newBuilder()
                                          .setFunnelEventFilter(
                                              FunnelEventFilter.newBuilder()
                                                  .setEventName("page_view"))))));
      requestBuilder
          .getFunnelBuilder()
          .addSteps(
              FunnelStep.newBuilder()
                  .setName("Purchase")
                  .setFilterExpression(
                      FunnelFilterExpression.newBuilder()
                          .setOrGroup(
                              FunnelFilterExpressionList.newBuilder()
                                  .addExpressions(
                                      FunnelFilterExpression.newBuilder()
                                          .setFunnelEventFilter(
                                              FunnelEventFilter.newBuilder()
                                                  .setEventName("purchase")))
                                  .addExpressions(
                                      FunnelFilterExpression.newBuilder()
                                          .setFunnelEventFilter(
                                              FunnelEventFilter.newBuilder()
                                                  .setEventName("in_app_purchase"))))));

      // Make the request.
      RunFunnelReportResponse response = analyticsData.runFunnelReport(requestBuilder.build());
      printRunFunnelReportResponse(response);
    }
  }

  /** Prints results of a runFunnelReport call. */
  static void printRunFunnelReportResponse(RunFunnelReportResponse response) {
    System.out.println("Report result:");
    System.out.println("=== FUNNEL VISUALIZATION ===");
    printFunnelSubReport(response.getFunnelVisualization());

    System.out.println("=== FUNNEL TABLE ===");
    printFunnelSubReport(response.getFunnelTable());
  }

  /** Prints the contents of a FunnelSubReport object. */
  private static void printFunnelSubReport(FunnelSubReport funnelSubReport) {
    System.out.println("Dimension headers:");
    for (DimensionHeader dimensionHeader : funnelSubReport.getDimensionHeadersList()) {
      System.out.println(dimensionHeader.getName());
    }
    System.out.println();

    System.out.println("Metric headers:");
    for (MetricHeader metricHeader : funnelSubReport.getMetricHeadersList()) {
      System.out.println(metricHeader.getName());
    }
    System.out.println();

    System.out.println("Dimension and metric values for each row in the report:");
    for (int rowIndex = 0; rowIndex < funnelSubReport.getRowsCount(); rowIndex++) {
      Row row = funnelSubReport.getRows(rowIndex);
      for (int fieldIndex = 0; fieldIndex < row.getDimensionValuesCount(); fieldIndex++) {
        System.out.printf(
            "%s: '%s'%n",
            funnelSubReport.getDimensionHeaders(fieldIndex).getName(),
            row.getDimensionValues(fieldIndex).getValue());
      }
      for (int fieldIndex = 0; fieldIndex < row.getMetricValuesCount(); fieldIndex++) {
        System.out.printf(
            "%s: '%s'%n",
            funnelSubReport.getMetricHeaders(fieldIndex).getName(),
            row.getMetricValues(fieldIndex).getValue());
      }
    }
    System.out.println();

    System.out.println("Sampling metadata for each date range:");
    for (int metadataIndex = 0;
        metadataIndex < funnelSubReport.getMetadata().getSamplingMetadatasCount();
        metadataIndex++) {
      SamplingMetadata samplingMetadata =
          funnelSubReport.getMetadata().getSamplingMetadatas(metadataIndex);
      System.out.printf(
          "Sampling metadata for date range #%d: samplesReadCount=%d, samplingSpaceSize=%d%n",
          metadataIndex,
          samplingMetadata.getSamplesReadCount(),
          samplingMetadata.getSamplingSpaceSize());
    }
  }
}

Python

from google.analytics.data_v1alpha import AlphaAnalyticsDataClient
from google.analytics.data_v1alpha.types import (
    DateRange,
    Dimension,
    Funnel,
    FunnelBreakdown,
    FunnelEventFilter,
    FunnelFieldFilter,
    FunnelFilterExpression,
    FunnelFilterExpressionList,
    FunnelStep,
    RunFunnelReportRequest,
    StringFilter,
)


def run_sample():
    """Runs the sample."""
    # TODO(developer): Replace this variable with your Google Analytics 4
    #  property ID before running the sample.
    property_id = "YOUR-GA4-PROPERTY-ID"
    run_funnel_report(property_id)


def run_funnel_report(property_id="YOUR-GA4-PROPERTY-ID"):
    """Runs a funnel query to build a report with 5 funnel steps.
      Step 1: First open/visit (event name is `first_open` or `first_visit`).
      Step 2: Organic visitors (`firstUserMedium` dimension contains the term
      "organic").
      Step 3: Session start (event name is `session_start`).
      Step 4: Screen/Page view (event name is `screen_view` or `page_view`).
      Step 5: Purchase (event name is `purchase` or `in_app_purchase`).

    The report configuration reproduces the default funnel report provided in
    the Funnel Exploration template of the Google Analytics UI.
    See more at https://support.google.com/analytics/answer/9327974
    """
    client = AlphaAnalyticsDataClient()

    request = RunFunnelReportRequest(
        property=f"properties/{property_id}",
        date_ranges=[DateRange(start_date="30daysAgo", end_date="today")],
        funnel_breakdown=FunnelBreakdown(
            breakdown_dimension=Dimension(name="deviceCategory")
        ),
        funnel=Funnel(
            steps=[
                FunnelStep(
                    name="First open/visit",
                    filter_expression=FunnelFilterExpression(
                        or_group=FunnelFilterExpressionList(
                            expressions=[
                                FunnelFilterExpression(
                                    funnel_event_filter=FunnelEventFilter(
                                        event_name="first_open"
                                    )
                                ),
                                FunnelFilterExpression(
                                    funnel_event_filter=FunnelEventFilter(
                                        event_name="first_visit"
                                    )
                                ),
                            ]
                        )
                    ),
                ),
                FunnelStep(
                    name="Organic visitors",
                    filter_expression=FunnelFilterExpression(
                        funnel_field_filter=FunnelFieldFilter(
                            field_name="firstUserMedium",
                            string_filter=StringFilter(
                                match_type=StringFilter.MatchType.CONTAINS,
                                case_sensitive=False,
                                value="organic",
                            ),
                        )
                    ),
                ),
                FunnelStep(
                    name="Session start",
                    filter_expression=FunnelFilterExpression(
                        funnel_event_filter=FunnelEventFilter(
                            event_name="session_start"
                        )
                    ),
                ),
                FunnelStep(
                    name="Screen/Page view",
                    filter_expression=FunnelFilterExpression(
                        or_group=FunnelFilterExpressionList(
                            expressions=[
                                FunnelFilterExpression(
                                    funnel_event_filter=FunnelEventFilter(
                                        event_name="screen_view"
                                    )
                                ),
                                FunnelFilterExpression(
                                    funnel_event_filter=FunnelEventFilter(
                                        event_name="page_view"
                                    )
                                ),
                            ]
                        )
                    ),
                ),
                FunnelStep(
                    name="Purchase",
                    filter_expression=FunnelFilterExpression(
                        or_group=FunnelFilterExpressionList(
                            expressions=[
                                FunnelFilterExpression(
                                    funnel_event_filter=FunnelEventFilter(
                                        event_name="purchase"
                                    )
                                ),
                                FunnelFilterExpression(
                                    funnel_event_filter=FunnelEventFilter(
                                        event_name="in_app_purchase"
                                    )
                                ),
                            ]
                        )
                    ),
                ),
            ]
        ),
    )
    response = client.run_funnel_report(request)
    print_run_funnel_report_response(response)


def print_funnel_sub_report(funnel_sub_report):
    """Prints the contents of a FunnelSubReport object."""
    print("Dimension headers:")
    for dimension_header in funnel_sub_report.dimension_headers:
        print(dimension_header.name)

    print("\nMetric headers:")
    for metric_header in funnel_sub_report.metric_headers:
        print(metric_header.name)

    print("\nDimensions and metric values for each row in the report:")
    for row_idx, row in enumerate(funnel_sub_report.rows):
        print("\nRow #{}".format(row_idx))
        for field_idx, dimension_value in enumerate(row.dimension_values):
            dimension_name = funnel_sub_report.dimension_headers[field_idx].name
            print("{}: '{}'".format(dimension_name, dimension_value.value))

        for field_idx, metric_value in enumerate(row.metric_values):
            metric_name = funnel_sub_report.metric_headers[field_idx].name
            print("{}: '{}'".format(metric_name, metric_value.value))

    print("\nSampling metadata for each date range:")
    for metadata_idx, metadata in enumerate(
        funnel_sub_report.metadata.sampling_metadatas
    ):
        print(
            "Sampling metadata for date range #{}: samplesReadCount={}, "
            "samplingSpaceSize={}".format(
                metadata_idx, metadata.samples_read_count, metadata.sampling_space_size
            )
        )


def print_run_funnel_report_response(response):
    """Prints results of a runFunnelReport call."""
    print("Report result:")
    print("=== FUNNEL VISUALIZATION ===")
    print_funnel_sub_report(response.funnel_visualization)

    print("=== FUNNEL TABLE ===")
    print_funnel_sub_report(response.funnel_table)