Script 1243: Tag Performance of Meta Ad Groups

Purpose

The Python script tags ad group performance based on specific business rules for reach, traffic, and conversion metrics.

To Elaborate

The script is designed to evaluate the performance of ad groups within Meta campaigns by applying predefined business rules. These rules categorize ad groups as “winning” or “losing” based on their performance metrics such as eCPM and CTR. The script processes data from a data source, applies these rules, and updates the performance tags accordingly. The business rules are defined for different campaign types, such as “Evergreen,” “GNO,” and “Formal,” and are applied to dimensions like “Reach,” “Traffic,” and “Conversion.” The script is intended to be run locally or on a server, with the ability to load data from a pickle file for local execution. The output is a modified dataset with updated performance tags, which can be used for further analysis or reporting.

Walking Through the Code

  1. Initialization and Setup
    • The script begins by determining whether it is running on a server or locally. If running locally, it loads data from a specified pickle file.
    • It imports necessary libraries such as pandas, numpy, and others for data manipulation and processing.
  2. Data Preparation
    • The script sets up the input DataFrame (inputDf) from the loaded data and prepares an output DataFrame (outputDf) by copying the input.
    • It defines the columns of interest for both input and output data, focusing on campaign performance metrics.
  3. Business Rules Definition
    • A list of dictionaries defines the business rules, specifying keywords, metrics, thresholds, and the corresponding dimension to update.
    • Each rule includes conditions for categorizing performance as “winning” or “losing” based on the specified thresholds.
  4. Applying Business Rules
    • The script iterates over each business rule, applying conditions to filter the DataFrame based on campaign names and performance metrics.
    • It updates the relevant dimensions in the DataFrame according to whether the performance metric exceeds or falls below the threshold.
  5. Output and Debugging
    • The script uses a utility function to select and output the changed data, focusing on specific columns.
    • If running locally, it writes the output and debug information to CSV files for further inspection.

Vitals

  • Script ID : 1243
  • Client ID / Customer ID: 1306920543 / 60268855
  • Action Type: Bulk Upload
  • Item Changed: AdGroup
  • Output Columns: Account, Campaign, Group, Reach, Traffic, Conversion
  • Linked Datasource: M1 Report
  • Reference Datasource: None
  • Owner: Michael Huang (mhuang@marinsoftware.com)
  • Created by Michael Huang on 2024-07-01 00:02
  • Last Updated by afarrokhi@marinsoftware.com on 2024-07-10 18:50
> See it in Action

Python Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
##
## name: Tag Performance of Meta Campaigns
## description: Tags the following Marin Dimensions based on performance
##  - Dimensions: Reach, Traffic, Conversion
##  
## 
## author: Michael S. Huang
## created: 2024-06-19
## 


########### START - Local Mode Config ###########
# Step 1: Uncomment download_preview_input flag and run Preview successfully with the Datasources you want
download_preview_input=False
# Step 2: In MarinOne, go to Scripts -> Preview -> Logs, download 'dataSourceDict' pickle file, and update pickle_path below
# pickle_path = ''
pickle_path = '/Users/mhuang/Downloads/pickle/windsor_fashions_meta_campaign_tagging_20240623.pkl'
# Step 3: Copy this script into local IDE with Python virtual env loaded with pandas and numpy.
# Step 4: Run locally with below code to init dataSourceDict

# determine if code is running on server or locally
def is_executing_on_server():
    try:
        # Attempt to access a known restricted builtin
        dict_items = dataSourceDict.items()
        return True
    except NameError:
        # NameError: dataSourceDict object is missing (indicating not on server)
        return False

local_dev = False

if is_executing_on_server():
    print("Code is executing on server. Skip init.")
elif len(pickle_path) > 3:
    print("Code is NOT executing on server. Doing init.")
    local_dev = True
    # load dataSourceDict via pickled file
    import pickle
    dataSourceDict = pickle.load(open(pickle_path, 'rb'))

    # print shape and first 5 rows for each entry in dataSourceDict
    for key, value in dataSourceDict.items():
        print(f"Shape of dataSourceDict[{key}]: {value.shape}")
        # print(f"First 5 rows of dataSourceDict[{key}]:\n{value.head(5)}")

    # set outputDf same as inputDf
    inputDf = dataSourceDict["1"]
    outputDf = inputDf.copy()

    # setup timezone
    import datetime
    # Chicago Timezone is GMT-5. Adjust as needed.
    CLIENT_TIMEZONE = datetime.timezone(datetime.timedelta(hours=-5))

    # import pandas
    import pandas as pd
    import numpy as np

    # other imports
    import re
    import urllib

    # import Marin util functions
    from marin_scripts_utils import tableize, select_changed

    # pandas settings
    pd.set_option('display.max_columns', None)  # Display all columns
    pd.set_option('display.max_colwidth', None)  # Display full content of each column

else:
    print("Running locally but no pickle path defined. dataSourceDict not loaded.")
    exit(1)
########### END - Local Mode Setup ###########

today = datetime.datetime.now(CLIENT_TIMEZONE).date()

# primary data source and columns
inputDf = dataSourceDict["1"]
RPT_COL_CAMPAIGN = 'Campaign'
RPT_COL_PUBLISHER = 'Publisher'
RPT_COL_ACCOUNT = 'Account'
RPT_COL_GROUP = 'Group'
RPT_COL_CAMPAIGN_TYPE = 'Campaign Type'
RPT_COL_CAMPAIGN_STATUS = 'Campaign Status'
RPT_COL_IMPR = 'Impr.'
RPT_COL_CLICKS = 'Clicks'
RPT_COL_PUB_COST = 'Pub. Cost $'
RPT_COL_ECPM = 'eCPM $'
RPT_COL_CTR = 'CTR %'
RPT_COL_REACH = 'Reach'
RPT_COL_TRAFFIC = 'Traffic'
RPT_COL_CONVERSION = 'Conversion'

# output columns and initial values
BULK_COL_ACCOUNT = 'Account'
BULK_COL_CAMPAIGN = 'Campaign'
BULK_COL_GROUP = 'Group'
BULK_COL_REACH = 'Reach'
BULK_COL_TRAFFIC = 'Traffic'
BULK_COL_CONVERSION = 'Conversion'
outputDf[BULK_COL_REACH] = "<<YOUR VALUE>>"
outputDf[BULK_COL_TRAFFIC] = "<<YOUR VALUE>>"
outputDf[BULK_COL_CONVERSION] = "<<YOUR VALUE>>"

# user code starts here

### Business Rules as Configurable Params

# Define the business rules in a dictionary
business_rules = [
    # 1. “Reach” dimension - campaign names containing “Evergreen” and “Reach” that perform below $2.21 CPM are “winning”, and if performing above $2.21 “losing”
    {
        "keywords": ["Evergreen", "Reach"],
        "metric": RPT_COL_ECPM,
        "threshold": 2.21,
        "dimension": BULK_COL_REACH,
        "value_high": "Losing",
        "value_low": "Winning"
    },
    # 2. “Reach” dimension - campaign names containing “GNO” and “Reach” that perform below $2.21 CPM are “winning”, and if performing above $2.21 “losing”
    {
        "keywords": ["GNO", "Reach"],
        "metric": RPT_COL_ECPM,
        "threshold": 2.21,
        "dimension": BULK_COL_REACH,
        "value_high": "Losing",
        "value_low": "Winning"
    },
    # 3. “Reach” dimension - campaign names containing “Formal” and “Reach” that perform below $2.36 CPM are “winning”, and if performing above $2.36 “losing”
    {
        "keywords": ["Formal", "Reach"],
        "metric": RPT_COL_ECPM,
        "threshold": 2.36,
        "dimension": BULK_COL_REACH,
        "value_high": "Losing",
        "value_low": "Winning"
    },
    # 4. “Traffic” dimension - campaign names containing “Evergreen” and “Traffic” that perform below 0.62% CTR are “losing”, and if performing above 0.62% “winning”
    {
        "keywords": ["Evergreen", "Traffic"],
        "metric": RPT_COL_CTR,
        "threshold": 0.0062,
        "dimension": BULK_COL_TRAFFIC,
        "value_high": "Winning",
        "value_low": "Losing"
    },
    # 5. “Traffic” dimension - campaign names containing “GNO” and “Traffic” that perform below 0.55% CTR are “losing”, and if performing above 0.55% CTR “winning”
    {
        "keywords": ["GNO", "Traffic"],
        "metric": RPT_COL_CTR,
        "threshold": 0.0055,
        "dimension": BULK_COL_TRAFFIC,
        "value_high": "Winning",
        "value_low": "Losing"
    },
    # 6. “Traffic” dimension - campaign names containing “Formal” and “Traffic” that perform below 0.77% CTR are “losing”, and if performing above 0.77% CTR “winning”
    {
        "keywords": ["Formal", "Traffic"],
        "metric": RPT_COL_CTR,
        "threshold": 0.0077,
        "dimension": BULK_COL_TRAFFIC,
        "value_high": "Winning",
        "value_low": "Losing"
    },
    # 7. “Conversion” dimension - campaign names containing “Evergreen” and “Conv” that perform below 0.33% CTR are “losing”, and if performing above 0.33% “winning”
    {
        "keywords": ["Evergreen", "Conv"],
        "metric": RPT_COL_CTR,
        "threshold": 0.0033,
        "dimension": BULK_COL_CONVERSION,
        "value_high": "Winning",
        "value_low": "Losing"
    },
    # 8. “Conversion” dimension - campaign names containing “GNO” and “Conv” that perform below 0.27% CTR are “losing”, and if performing above 0.27% “winning”
    {
        "keywords": ["GNO", "Conv"],
        "metric": RPT_COL_CTR,
        "threshold": 0.0027,
        "dimension": BULK_COL_CONVERSION,
        "value_high": "Winning",
        "value_low": "Losing"
    },
    # 9. “Conversion” dimension - campaign names containing “Formal” and “Conv” that perform below 0.45% CTR are “losing”, and if performing above 0.45% “winning”
    {
        "keywords": ["Formal", "Conv"],
        "metric": RPT_COL_CTR,
        "threshold": 0.0045,
        "dimension": BULK_COL_CONVERSION,
        "value_high": "Winning",
        "value_low": "Losing"
    }
]

print("inputDf.shape", inputDf.shape)
print("inputDf.dtypes", inputDf.dtypes)
print("inputDf sample", inputDf.head())

originalDf = inputDf.copy()

# Apply the business rules to set dimensions
for rule in business_rules:
    # Build the condition to filter DataFrame based on all keywords
    condition = inputDf[RPT_COL_CAMPAIGN].apply(lambda x: all(keyword in x for keyword in rule["keywords"]))

    # Apply the rule based on the metric threshold
    inputDf.loc[condition & (inputDf[rule["metric"]] > rule["threshold"]), rule["dimension"]] = rule["value_high"]
    inputDf.loc[condition & (inputDf[rule["metric"]] <= rule["threshold"]), rule["dimension"]] = rule["value_low"]

outputDf, debugDf = select_changed(inputDf, 
                                   originalDf,
                                   diff_cols = [RPT_COL_REACH, RPT_COL_TRAFFIC, RPT_COL_CONVERSION],
                                   select_cols = [RPT_COL_ACCOUNT, RPT_COL_CAMPAIGN, RPT_COL_GROUP, RPT_COL_REACH, RPT_COL_TRAFFIC, RPT_COL_CONVERSION],
                                   merged_cols = [RPT_COL_ACCOUNT, RPT_COL_CAMPAIGN, RPT_COL_GROUP]
                                   )

print("outputDf.shape", outputDf.shape)

### local debug

if local_dev:
    output_filename = 'outputDf.csv'
    outputDf.to_csv(output_filename, index=False)
    print(f"Local Dev: Output written to: {output_filename}")

    debug_filename = 'debugDf.csv'
    debugDf.to_csv(debug_filename, index=False)
    print(f"Local Dev: Debug written to: {debug_filename}")


Post generated on 2024-11-27 06:58:46 GMT

comments powered by Disqus