diff --git a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/campaignmanagement/CreateExperiment.java b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/experiments/CreateSearchCustomExperiment.java similarity index 88% rename from google-ads-examples/src/main/java/com/google/ads/googleads/examples/campaignmanagement/CreateExperiment.java rename to google-ads-examples/src/main/java/com/google/ads/googleads/examples/experiments/CreateSearchCustomExperiment.java index 03dcd7772..b9c854df0 100644 --- a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/campaignmanagement/CreateExperiment.java +++ b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/experiments/CreateSearchCustomExperiment.java @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package com.google.ads.googleads.examples.campaignmanagement; +package com.google.ads.googleads.examples.experiments; import static com.google.ads.googleads.examples.utils.CodeSampleHelper.getPrintableDateTime; @@ -47,12 +47,19 @@ import java.util.List; /** - * This example creates a new experiment, experiment arms, and demonstrates how to modify the draft - * campaign as well as begin the experiment. + * This example creates a standard, system-managed campaign experiment. + * + *
It demonstrates how to create an experiment, configure its control and treatment arms (where + * the treatment arm automatically generates a draft campaign), modify the system-generated draft + * campaign, and schedule the experiment. + * + *
Note: This standard draft-based workflow does not apply to all experiment types (e.g., + * intra-campaign or asset optimization experiments) that do not use system-generated treatment campaign + * copies. */ -public class CreateExperiment { +public class CreateSearchCustomExperiment { - private static class CreateExperimentParams extends CodeSampleParams { + private static class CreateSearchCustomExperimentParams extends CodeSampleParams { @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true) private Long customerId; @@ -62,7 +69,7 @@ private static class CreateExperimentParams extends CodeSampleParams { } public static void main(String[] args) { - CreateExperimentParams params = new CreateExperimentParams(); + CreateSearchCustomExperimentParams params = new CreateSearchCustomExperimentParams(); if (!params.parseArguments(args)) { throw new IllegalArgumentException("Invalid or missing command line arguments"); } @@ -80,7 +87,7 @@ public static void main(String[] args) { } try { - new CreateExperiment().runExample(googleAdsClient, params.customerId, params.baseCampaignId); + new CreateSearchCustomExperiment().runExample(googleAdsClient, params.customerId, params.baseCampaignId); } catch (GoogleAdsException gae) { // GoogleAdsException is the base class for most exceptions thrown by an API request. // Instances of this exception have a message and a GoogleAdsFailure that contains a @@ -130,6 +137,9 @@ private String createExperimentResource(GoogleAdsClient googleAdsClient, long cu Experiment.newBuilder() // Name must be unique. .setName("Example Experiment #" + getPrintableDateTime()) + // We specify SEARCH_CUSTOM to create a standard search campaign experiment. + // This type uses a standard draft-based workflow where the system automatically + // creates a draft/in-design campaign for the treatment arm. .setType(ExperimentType.SEARCH_CUSTOM) .setSuffix("[experiment]") .setStatus(ExperimentStatus.SETUP) @@ -170,8 +180,8 @@ private String createExperimentArms( operations.add( ExperimentArmOperation.newBuilder() .setCreate( - // The non-"control" arm, also called a "treatment" arm, will automatically - // generate draft campaigns that you can modify before starting the experiment. + // In standard campaign experiments, creating the treatment arm automatically + // generates a draft campaign that you can modify before starting the experiment. ExperimentArm.newBuilder() .setControl(false) .setExperiment(experiment) diff --git a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/experiments/GetExperimentPerformance.java b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/experiments/GetExperimentPerformance.java new file mode 100644 index 000000000..b45641ab8 --- /dev/null +++ b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/experiments/GetExperimentPerformance.java @@ -0,0 +1,266 @@ +package com.google.ads.googleads.examples.experiments; + +import com.beust.jcommander.Parameter; +import com.google.ads.googleads.examples.utils.ArgumentNames; +import com.google.ads.googleads.examples.utils.CodeSampleParams; +import com.google.ads.googleads.lib.GoogleAdsClient; +import com.google.ads.googleads.v24.enums.BudgetDeliveryMethodEnum.BudgetDeliveryMethod; +import com.google.ads.googleads.v24.enums.ExperimentTypeEnum.ExperimentType; +import com.google.ads.googleads.v24.errors.GoogleAdsError; +import com.google.ads.googleads.v24.errors.GoogleAdsException; +import com.google.ads.googleads.v24.resources.CampaignBudget; +import com.google.ads.googleads.v24.common.Metrics; +import com.google.ads.googleads.v24.services.CampaignBudgetMapping; +import com.google.ads.googleads.v24.services.CampaignBudgetOperation; +import com.google.ads.googleads.v24.services.CampaignBudgetServiceClient; +import com.google.ads.googleads.v24.services.ExperimentServiceClient; +import com.google.ads.googleads.v24.services.GoogleAdsRow; +import com.google.ads.googleads.v24.services.GoogleAdsServiceClient; +import com.google.ads.googleads.v24.services.MutateCampaignBudgetsResponse; +import com.google.ads.googleads.v24.services.SearchGoogleAdsRequest; +import com.google.api.gax.longrunning.OperationFuture; +import com.google.protobuf.Empty; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Collections; +import java.util.UUID; + +/** + * This example illustrates how to retrieve performance metrics for an experiment. + * + *
It shows how to query statistical significance metrics for the experiment,
+ * and how to execute actions such as promoting, ending, or graduating an experiment.
+ */
+public class GetExperimentPerformance {
+ private static final double P_VALUE_THRESHOLD = 0.05;
+
+ private static class GetExperimentPerformanceParams extends CodeSampleParams {
+
+ @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true)
+ private Long customerId;
+
+ @Parameter(names = ArgumentNames.EXPERIMENT_ID, required = true)
+ private Long experimentId;
+ }
+
+ public static void main(String[] args) {
+ GetExperimentPerformanceParams params = new GetExperimentPerformanceParams();
+ if (!params.parseArguments(args)) {
+ throw new IllegalArgumentException("Invalid or missing command line arguments");
+ }
+
+ GoogleAdsClient googleAdsClient = null;
+ try {
+ googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build();
+ } catch (FileNotFoundException fnfe) {
+ System.err.printf(
+ "Failed to load GoogleAdsClient configuration from file. Exception: %s%n", fnfe);
+ System.exit(1);
+ } catch (IOException ioe) {
+ System.err.printf("Failed to create GoogleAdsClient. Exception: %s%n", ioe);
+ System.exit(1);
+ }
+
+ try {
+ new GetExperimentPerformance().runExample(googleAdsClient, params.customerId, params.experimentId);
+ } catch (GoogleAdsException gae) {
+ System.err.printf(
+ "Request ID %s failed due to GoogleAdsException. Underlying errors:%n",
+ gae.getRequestId());
+ int i = 0;
+ for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {
+ System.err.printf(" Error %d: %s%n", i++, googleAdsError);
+ }
+ System.exit(1);
+ }
+ }
+
+ /**
+ * Runs the example.
+ *
+ * @param googleAdsClient the googleAdsClient.
+ * @param customerId the customer ID.
+ * @param experimentId the experiment ID.
+ */
+ private void runExample(GoogleAdsClient googleAdsClient, long customerId, long experimentId) {
+ try (GoogleAdsServiceClient googleAdsServiceClient =
+ googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {
+
+ String query =
+ String.format(
+ "SELECT "
+ + "experiment.resource_name, "
+ + "experiment.name, "
+ + "experiment.experiment_id, "
+ + "experiment.type, "
+ + "metrics.conversions_absolute_change_p_value, "
+ + "metrics.conversions_absolute_change_point_estimate, "
+ + "metrics.conversions_absolute_change_margin_of_error, "
+ + "metrics.clicks_p_value, "
+ + "metrics.clicks_point_estimate, "
+ + "metrics.clicks_margin_of_error "
+ + "FROM experiment "
+ + "WHERE experiment.experiment_id = %d",
+ experimentId);
+
+ SearchGoogleAdsRequest request =
+ SearchGoogleAdsRequest.newBuilder()
+ .setCustomerId(Long.toString(customerId))
+ .setQuery(query)
+ .build();
+
+ GoogleAdsServiceClient.SearchPagedResponse response = googleAdsServiceClient.search(request);
+
+ boolean experimentFound = false;
+ for (GoogleAdsRow row : response.iterateAll()) {
+ experimentFound = true;
+ System.out.printf("Found experiment: %s%n", row.getExperiment().getName());
+ System.out.printf(" Resource Name: %s%n", row.getExperiment().getResourceName());
+
+ evaluateExperiment(googleAdsClient, customerId, row);
+ }
+
+ if (!experimentFound) {
+ System.out.printf("No experiment found for experiment ID: %d%n", experimentId);
+ }
+ }
+ }
+
+ // [START get_experiment_performance_1]
+ private void evaluateExperiment(GoogleAdsClient googleAdsClient, long customerId, GoogleAdsRow row) {
+ Metrics metrics = row.getMetrics();
+ String experimentResourceName = row.getExperiment().getResourceName();
+
+ double convPValue = metrics.getConversionsAbsoluteChangePValue();
+ double convLift = metrics.getConversionsAbsoluteChangePointEstimate();
+ double convError = metrics.getConversionsAbsoluteChangeMarginOfError();
+ double convLowerBound = convLift - convError;
+
+ if (convPValue <= P_VALUE_THRESHOLD) {
+ if (convLowerBound > 0) {
+ System.out.printf(
+ "Significant Success: Conversions increased. Even at the lower bound, the lift is %.2f. Promoting changes.%n",
+ convLowerBound);
+ promoteExperiment(googleAdsClient, customerId, experimentResourceName);
+ return;
+ } else if ((convLift + convError) < 0) {
+ System.out.printf(
+ "Significant Decline: Even the upper bound (%.2f) is below zero. Ending experiment.%n",
+ convLift + convError);
+ endExperiment(googleAdsClient, customerId, experimentResourceName);
+ return;
+ }
+ }
+
+ double clickPValue = metrics.getClicksPValue();
+ double clickLift = metrics.getClicksPointEstimate();
+ double clickError = metrics.getClicksMarginOfError();
+ double clickLowerBound = clickLift - clickError;
+
+ if (clickPValue <= P_VALUE_THRESHOLD && clickLowerBound > 0) {
+ System.out.printf(
+ "Click volume is significantly up (+%.1f%%). Graduating treatment for further manual analysis.%n",
+ clickLift * 100);
+
+ ExperimentType experimentType = row.getExperiment().getType();
+ if (experimentType != ExperimentType.ADOPT_BROAD_MATCH_KEYWORDS
+ && experimentType != ExperimentType.ADOPT_AI_MAX) {
+ graduateExperiment(googleAdsClient, customerId, experimentResourceName);
+ } else {
+ System.out.println(
+ "Intra-campaign trial detected: Graduation is not supported because there is only one campaign. Continuing to run to gather more conversion data.");
+ }
+ } else {
+ System.out.printf(
+ "Inconclusive: No significant lift in Conversions (p=%.2f) or Clicks (p=%.2f). Current estimated lift: %.2f +/- %.2f. Continue running.%n",
+ convPValue, clickPValue, convLift, convError);
+ }
+ }
+ // [END get_experiment_performance_1]
+
+ private void promoteExperiment(GoogleAdsClient googleAdsClient, long customerId, String experimentResourceName) {
+ try (ExperimentServiceClient experimentServiceClient =
+ googleAdsClient.getLatestVersion().createExperimentServiceClient()) {
+ OperationFuture