Script 1199: Tag Performance of Meta Campaigns

Purpose

The Python script tags Marin Dimensions for Meta campaigns based on performance metrics such as CPM and CTR.

To Elaborate

The script is designed to evaluate the performance of Meta advertising campaigns by tagging them according to specific business rules. These rules are based on campaign names and their performance metrics, such as Cost Per Mille (CPM) and Click-Through Rate (CTR). The script categorizes campaigns into “winning” or “losing” based on whether they meet or fall short of predefined thresholds for these metrics. The business rules are applied to different dimensions like Reach, Traffic, and Conversion, with specific keywords in campaign names triggering the evaluation. This automated tagging helps in quickly identifying the effectiveness of various campaigns, enabling marketers to make informed decisions about their advertising strategies.

Walking Through the Code

  1. Local Mode Configuration:
    • The script begins by setting up a local development environment, checking if it is running on a server or locally. If local, it loads a data source dictionary from a pickle file for further processing.
  2. Data Preparation:
    • The script imports necessary libraries and sets up the data frame (inputDf) from the data source dictionary. It also prepares an output data frame (outputDf) to store results.
  3. Business Rules Definition:
    • A list of dictionaries defines the business rules, specifying keywords, metrics, thresholds, and dimensions for tagging campaigns as “winning” or “losing”. These rules are user-configurable and can be adjusted as needed.
  4. Applying Business Rules:
    • The script iterates over each rule, applying conditions to filter campaigns based on keywords and performance metrics. It updates the inputDf with the appropriate tags (“winning” or “losing”) based on whether the campaign’s performance metric exceeds or falls below the threshold.
  5. Output Generation:
    • The script uses a utility function to compare the modified data frame with the original, selecting only the changed rows for output. It then writes the results to CSV files if running in a local development environment.

Vitals

  • Script ID : 1199
  • Client ID / Customer ID: 1306920543 / 60268855
  • Action Type: Bulk Upload
  • Item Changed: Campaign
  • Output Columns: Account, Campaign, Reach, Conversion, Traffic
  • Linked Datasource: M1 Report
  • Reference Datasource: None
  • Owner: Michael Huang (mhuang@marinsoftware.com)
  • Created by Michael Huang on 2024-06-19 17:38
  • Last Updated by afarrokhi@marinsoftware.com on 2024-07-10 18:51
> 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
##
## 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_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_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_REACH, RPT_COL_TRAFFIC, RPT_COL_CONVERSION],
                                   merged_cols = [RPT_COL_ACCOUNT, RPT_COL_CAMPAIGN]
                                   )

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