Analysis of RNI's digital ads on Meta's platforms

¶

The National Rally of Independents (Arabic: التجمع الوطني للأحرار; French: Rassemblement National des Indépendants) is one of the political parties in Morocco. Since September 2021, it has been the country's ruling party after winning the last elections and the president, Mr. Aziz Akhannouch, is the current Moroccan prime minister.

A quick glance at their social media activity on meta's platforms, it is evident they are the most active political, with overall spendings on sponsored posts going up to $425662, far surpassing all other parties.

For that reason, in this study we will use data extracted from meta ads library(*) for the duration between March 11th, 2021 and August 12th, 2023 to analyze their activity on both Facebook and Instagram. The main objective of this study is to quantify the RNI digital strategy while also identifying any existing trends. To achieve that goal, we used Python's Pandas module on a Jupyter notebook for data manipulation and analysis. As for data visualization, we created a dashboard using Microsoft's Power BI.

This study is based on 2 CSV files exported from The Meta Ads Library. The RNI.CSV file contains the data of all the ads created and launched by the RNI during the period between March 11th 2021 and August 12th 2023. While General.CSV contains data about advertisers that ran sponsored content concerning social issues, elections or politics on Meta technologies over the same period.

The CSV files contains the following fields:

  1. RNI.csv
  • ad_creation_time: The day the ad was created.
  • ad_delivery_start_time: The day the ad was put online.
  • ad_delivery_stop_time: The date the ad was turned offline.
  • ad_creative_bodies: The caption of the sponsored post.
  • ad_creative_link_titles: The titles which appear in the call to action section for each unique ad card of the ad.
  • impressions: The number of times an ad was on a screen, which may include multiple views by the same people.
  • spend: The estimated total amount of money spent on an ad during its schedule.
  • publisher_platforms: The platforms chosen for diffusing ads.
  • estimated_audience_size: Estimated audience size is an estimate of how many Accounts Center accounts met an advertiser's targeting criteria when the ad was created. This is not an estimate of how many people saw an ad and is not designed to match population or census data.
  • languages: the language used in the ads
  1. General.csv
  • Amount spent (USD): The amount shown for the report is equal to the amount spent on impressions for ads in the Ad Library that reached people on Meta technologies in Morocco for the period between March 11th 2021 and august 12th 2023.
  • Number of ads in Library: The total number of ads in the Ad Library that were delivered on Meta technologies in the selected country from ad accounts that had the relevant ad account currency.

(*) The Meta Ads Library is a searchable database. It includes ads about social issues, elections or politics that have run on Meta technologies. This report is a summary of the Ad Library and includes data for ads that have been viewed by people in the selected country for the date range specified.

In [121]:
# Importing the required python libraries
import pandas as pd
import warnings
warnings.filterwarnings('ignore')
import nltk
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
from nltk.util import ngrams
from collections import Counter
In [15]:
# Importing the csv file containing RNI ads data into a dataframe
path = "RNI.csv"
RNI = pd.read_csv(path)
RNI.head()
Out[15]:
ad_archive_id page_id page_name ad_creation_time ad_delivery_start_time ad_delivery_stop_time byline ad_creative_bodies ad_creative_link_titles ad_creative_link_captions ad_creative_link_descriptions impressions spend currency demographic_distribution delivery_by_region publisher_platforms estimated_audience_size languages
0 322927090074137 117281538350746 حزب التجمع الوطني للأحرار -Rassemblement Natio... 2023-07-26 2023-07-26 2023-07-29 Rassemblement National des Indépendants أكد محمد أوجار، عضو المكتب السياسي لحزب التجمع... أوجار: دسائس ومناورات الجيران لن تنال من وفاء ... NaN NaN lower_bound: 350000, upper_bound: 399999 lower_bound: 200, upper_bound: 299 USD {"age":"35-44","gender":"unknown","percentage"... {"region":"Unknown","percentage":0.000102},{"r... facebook lower_bound: 500001, upper_bound: 1000000 ar
1 562536185911310 117281538350746 حزب التجمع الوطني للأحرار -Rassemblement Natio... 2023-07-26 2023-07-26 2023-07-29 Rassemblement National des Indépendants بعد زهاء 25 سنة من العمل الجماعي، يكرس محمد ال... العايدي: ولدت وترعرعت بجماعة كطاية وهدفي تحقيق... NaN NaN lower_bound: 500000, upper_bound: 599999 lower_bound: 200, upper_bound: 299 USD {"age":"45-54","gender":"unknown","percentage"... {"region":"Unknown","percentage":5.6e-5},{"reg... facebook lower_bound: 500001, upper_bound: 1000000 ar
2 660134422682212 117281538350746 حزب التجمع الوطني للأحرار -Rassemblement Natio... 2023-07-26 2023-07-26 2023-07-29 Rassemblement National des Indépendants عبّر محمد بنبيكة، رئيس مجلس جماعة واد زم عن سع... NaN NaN NaN lower_bound: 350000, upper_bound: 399999 lower_bound: 200, upper_bound: 299 USD {"age":"18-24","gender":"unknown","percentage"... {"region":"Unknown","percentage":2.7e-5},{"reg... facebook lower_bound: 500001, upper_bound: 1000000 ar
3 1491877614917081 117281538350746 حزب التجمع الوطني للأحرار -Rassemblement Natio... 2023-07-26 2023-07-26 2023-07-29 Rassemblement National des Indépendants الرباط، مجلس النواب \n🙏 أكد رئيس الحكومة، عزيز... أخنوش: وضعية المرأة عرفت في عهد جلالة الملك ثو... NaN NaN lower_bound: 450000, upper_bound: 499999 lower_bound: 200, upper_bound: 299 USD {"age":"55-64","gender":"unknown","percentage"... {"region":"Unknown","percentage":5.6e-5},{"reg... facebook lower_bound: 500001, upper_bound: 1000000 ar
4 1059402098799644 117281538350746 حزب التجمع الوطني للأحرار -Rassemblement Natio... 2023-07-26 2023-07-26 2023-07-29 Rassemblement National des Indépendants يدرك حاتم برقية، رئيس جماعة سيدي علال التازي، ... برقية: هدفي القرب من الساكنة وجعل علال التازي ... NaN NaN lower_bound: 400000, upper_bound: 449999 lower_bound: 200, upper_bound: 299 USD {"age":"45-54","gender":"unknown","percentage"... {"region":"Unknown","percentage":6.5e-5},{"reg... facebook lower_bound: 500001, upper_bound: 1000000 ar
In [21]:
# Importing the csv file overall political and social ads data into a dataframe
path = "General.csv"
General_stats = pd.read_csv(path)
General_stats.head()
Out[21]:
Page ID Page name Disclaimer Amount spent (USD) Number of ads in Library
0 117281538350746 حزب التجمع الوطني للأحرار -Rassemblement Natio... Rassemblement National des Indépendants 425662 5896
1 147084112022235 La Nouvelle Tribune La Nouvelle Tribune 315542 1149
2 107627395144123 الحكومة المغربية الحكومة المغربية 94219 1847
3 196448427590617 Parti de l'Istiqlal - حزب الاستقلال Parti Istiqlal 40549 355
4 1117512094962616 Ministère de l’Industrie et du Commerce MCINET 31866 279

Data cleaning and processing¶

Removing useless columns¶

In [33]:
RNI_clean = RNI.drop(columns = ['page_id','page_name','ad_creation_time','byline','demographic_distribution','delivery_by_region','currency','ad_creative_link_captions'])
General_clean = General_stats.drop(columns = ['Disclaimer','Page ID'])

Converting date columns into the correct data types¶

In [107]:
## Dates
RNI_clean[['ad_delivery_start_time','ad_delivery_stop_time']] = RNI_clean[['ad_delivery_start_time','ad_delivery_stop_time']].apply(pd.to_datetime)
RNI_clean.info()

## Integers
General_top20 = General_clean.head(20)
General_top20[['Amount spent (USD)','Number of ads in Library']] = General_top20[['Amount spent (USD)','Number of ads in Library']].apply(pd.to_numeric)
General_top20.info()
<class 'pandas.core.frame.DataFrame'>
Index: 5986 entries, 0 to 5985
Data columns (total 12 columns):
 #   Column                         Non-Null Count  Dtype          
---  ------                         --------------  -----          
 0   ad_archive_id                  5986 non-null   int64          
 1   ad_delivery_start_time         5986 non-null   datetime64[ns] 
 2   ad_delivery_stop_time          5985 non-null   datetime64[ns] 
 3   ad_creative_bodies             5199 non-null   object         
 4   ad_creative_link_titles        2149 non-null   object         
 5   ad_creative_link_descriptions  376 non-null    object         
 6   impressions                    5986 non-null   object         
 7   spend                          5986 non-null   object         
 8   publisher_platforms            5986 non-null   object         
 9   estimated_audience_size        5986 non-null   object         
 10  languages                      5881 non-null   object         
 11  ad_duration                    5985 non-null   timedelta64[ns]
dtypes: datetime64[ns](2), int64(1), object(8), timedelta64[ns](1)
memory usage: 608.0+ KB
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 20 entries, 0 to 19
Data columns (total 3 columns):
 #   Column                    Non-Null Count  Dtype 
---  ------                    --------------  ----- 
 0   Page name                 20 non-null     object
 1   Amount spent (USD)        20 non-null     int64 
 2   Number of ads in Library  20 non-null     int64 
dtypes: int64(2), object(1)
memory usage: 612.0+ bytes

Removing data entries from before March 11th 2021¶

In [37]:
RNI_clean =  RNI_clean[RNI_clean['ad_delivery_start_time']>'2021-03-10']
RNI_clean.sort_values(by=['ad_delivery_start_time'])
Out[37]:
ad_archive_id ad_delivery_start_time ad_delivery_stop_time ad_creative_bodies ad_creative_link_titles ad_creative_link_descriptions impressions spend publisher_platforms estimated_audience_size languages
5985 434618161148172 2021-03-14 2021-03-16 أمام تزايد الضغط الناتج عن الوشايات الكاذبة بأ... NaN NaN lower_bound: 30000, upper_bound: 34999 lower_bound: 0, upper_bound: 99 facebook,audience_network lower_bound: 1000001 ar
5984 349309746436365 2021-03-18 2021-03-18 تتابعون مباشرة ثالث حلقات برنامج لكم الكلمة حو... برنامج لكم الكلمة - الحلقة الثالثة NaN lower_bound: 100000, upper_bound: 124999 lower_bound: 100, upper_bound: 199 facebook,messenger lower_bound: 1000001 ar
5982 259401015842630 2021-03-21 2021-03-21 NaN NaN NaN lower_bound: 4000, upper_bound: 4999 lower_bound: 0, upper_bound: 99 instagram lower_bound: 100001, upper_bound: 500000 ar
5983 262481268832944 2021-03-21 2021-03-21 NaN NaN NaN lower_bound: 4000, upper_bound: 4999 lower_bound: 0, upper_bound: 99 instagram lower_bound: 100001, upper_bound: 500000 ar
5974 265923038355628 2021-03-22 2021-03-28 NaN NaN NaN lower_bound: 25000, upper_bound: 29999 lower_bound: 0, upper_bound: 99 instagram lower_bound: 100001, upper_bound: 500000 ar
... ... ... ... ... ... ... ... ... ... ... ...
4 1059402098799644 2023-07-26 2023-07-29 يدرك حاتم برقية، رئيس جماعة سيدي علال التازي، ... برقية: هدفي القرب من الساكنة وجعل علال التازي ... NaN lower_bound: 400000, upper_bound: 449999 lower_bound: 200, upper_bound: 299 facebook lower_bound: 500001, upper_bound: 1000000 ar
3 1491877614917081 2023-07-26 2023-07-29 الرباط، مجلس النواب \n🙏 أكد رئيس الحكومة، عزيز... أخنوش: وضعية المرأة عرفت في عهد جلالة الملك ثو... NaN lower_bound: 450000, upper_bound: 499999 lower_bound: 200, upper_bound: 299 facebook lower_bound: 500001, upper_bound: 1000000 ar
2 660134422682212 2023-07-26 2023-07-29 عبّر محمد بنبيكة، رئيس مجلس جماعة واد زم عن سع... NaN NaN lower_bound: 350000, upper_bound: 399999 lower_bound: 200, upper_bound: 299 facebook lower_bound: 500001, upper_bound: 1000000 ar
1 562536185911310 2023-07-26 2023-07-29 بعد زهاء 25 سنة من العمل الجماعي، يكرس محمد ال... العايدي: ولدت وترعرعت بجماعة كطاية وهدفي تحقيق... NaN lower_bound: 500000, upper_bound: 599999 lower_bound: 200, upper_bound: 299 facebook lower_bound: 500001, upper_bound: 1000000 ar
0 322927090074137 2023-07-26 2023-07-29 أكد محمد أوجار، عضو المكتب السياسي لحزب التجمع... أوجار: دسائس ومناورات الجيران لن تنال من وفاء ... NaN lower_bound: 350000, upper_bound: 399999 lower_bound: 200, upper_bound: 299 facebook lower_bound: 500001, upper_bound: 1000000 ar

5986 rows × 11 columns

Data analysis¶

I. Keywords analysis¶

In this part, we will separate the text used in the ad campaigns to extract relevant keywords. And while this method may not be very effective considering the massive volume of the data, it can still serve as a way to quickly detect some relevent topic

1. Keyword extraction and exporting¶

In [127]:
RNI_keywords = RNI_clean[['ad_creative_bodies', 'ad_creative_link_titles']]
RNI_keywords.to_csv('Output/RNI_text.csv', index=False, encoding='utf-8')
RNI_keywords.head()
Out[127]:
ad_creative_bodies ad_creative_link_titles
0 أكد محمد أوجار، عضو المكتب السياسي لحزب التجمع... أوجار: دسائس ومناورات الجيران لن تنال من وفاء ...
1 بعد زهاء 25 سنة من العمل الجماعي، يكرس محمد ال... العايدي: ولدت وترعرعت بجماعة كطاية وهدفي تحقيق...
2 عبّر محمد بنبيكة، رئيس مجلس جماعة واد زم عن سع... NaN
3 الرباط، مجلس النواب \n🙏 أكد رئيس الحكومة، عزيز... أخنوش: وضعية المرأة عرفت في عهد جلالة الملك ثو...
4 يدرك حاتم برقية، رئيس جماعة سيدي علال التازي، ... برقية: هدفي القرب من الساكنة وجعل علال التازي ...

2. Finding the top 30 most frequent words and bigrams within the ad_creative_bodies column¶

In [124]:
# Tokenize and preprocess Arabic text
stop_words = set(stopwords.words('arabic'))
RNI_keywords['tokenized_bodies'] = RNI_keywords['ad_creative_bodies'].apply(lambda x: [word for word in word_tokenize(str(x)) if word not in stop_words and word.isalpha()])

# Count word frequencies
word_counts = Counter(word for words in RNI_keywords['tokenized_bodies'] for word in words)

# Display most common words
print("Most Common Words:")
print(word_counts.most_common(30))
print()

# Create bigrams for analysis
RNI_keywords['bigrams'] = RNI_keywords['tokenized_bodies'].apply(lambda x: list(ngrams(x, 2)))

# Count bigram frequencies
bigram_counts = Counter(bigram for bigrams_list in RNI_keywords['bigrams'] for bigram in bigrams_list)

# Display most common bigrams
print("Most Common Bigrams:")
print(bigram_counts.most_common(30))
Most Common Words:
[('الوطني', 1335), ('عزيز', 1111), ('الحكومة', 1081), ('خلال', 999), ('رئيس', 962), ('الأحرار', 950), ('التجمع', 888), ('nan', 787), ('المغرب', 676), ('اللي', 672), ('برنامج', 655), ('يوم', 624), ('أخنوش', 619), ('مدينة', 614), ('الحزب', 573), ('ديال', 547), ('للأحرار', 539), ('محمد', 477), ('السياسي', 476), ('عضو', 433), ('الوطنية', 433), ('أكد', 432), ('اليوم', 421), ('المكتب', 415), ('الشباب', 407), ('حزب', 386), ('الجهوي', 381), ('الفيدرالية', 334), ('de', 331), ('بجهة', 316)]

Most Common Bigrams:
[(('التجمع', 'الوطني'), 876), (('عزيز', 'أخنوش'), 564), (('الوطني', 'للأحرار'), 496), (('يوم', 'مدينة'), 480), (('المكتب', 'السياسي'), 399), (('عزيز', 'رئيس'), 386), (('عضو', 'المكتب'), 353), (('حزب', 'التجمع'), 331), (('الفيدرالية', 'الوطنية'), 305), (('لحزب', 'التجمع'), 244), (('رئيس', 'الحكومة'), 232), (('برنامج', 'الأحرار'), 214), (('للتجمع', 'الوطني'), 206), (('الملك', 'محمد'), 179), (('السياسي', 'لحزب'), 178), (('برنامج', 'يوم'), 160), (('رئيس', 'حزب'), 150), (('أكد', 'عزيز'), 148), (('لقاء', 'يوم'), 142), (('villes', 'jours'), 142), (('السياسي', 'للتجمع'), 137), (('خلاصات', 'لقاء'), 137), (('الجهوي', 'للحزب'), 134), (('المؤتمر', 'الجهوي'), 134), (('المنتدى', 'الجهوي'), 126), (('خلاصات', 'يوم'), 121), (('participants', 'à'), 121), (('de', 'la'), 120), (('الوطنية', 'للشبيبة'), 118), (('أخنوش', 'رئيس'), 118)]

3. Finding the top 30 most frequent words and bigrams within the ad_creative_link_titles column¶

In [125]:
# Tokenize and preprocess Arabic text
stop_words = set(stopwords.words('arabic'))
RNI_keywords['tokenized_bodies'] = RNI_keywords['ad_creative_link_titles'].apply(lambda x: [word for word in word_tokenize(str(x)) if word not in stop_words and word.isalpha()])

# Count word frequencies
word_counts = Counter(word for words in RNI_keywords['tokenized_bodies'] for word in words)

# Display most common words
print("Most Common Words:")
print(word_counts.most_common(30))
print()

# Create bigrams for analysis
RNI_keywords['bigrams'] = RNI_keywords['tokenized_bodies'].apply(lambda x: list(ngrams(x, 2)))

# Count bigram frequencies
bigram_counts = Counter(bigram for bigrams_list in RNI_keywords['bigrams'] for bigram in bigrams_list)

# Display most common bigrams
print("Most Common Bigrams:")
print(bigram_counts.most_common(30))
Most Common Words:
[('nan', 3837), ('التجمع', 449), ('الوطني', 437), ('للأحرار', 409), ('أخنوش', 308), ('الأحرار', 285), ('عزيز', 225), ('حزب', 210), ('خلاصات', 195), ('National', 141), ('des', 141), ('Indépendants', 141), ('برنامج', 134), ('الوطنية', 133), ('الحكومة', 118), ('partirni', 115), ('Instagram', 115), ('photos', 115), ('and', 115), ('videos', 115), ('اللي', 113), ('RNI', 113), ('الفيدرالية', 105), ('البرنامج', 95), ('للفنانين', 86), ('التجمعين', 85), ('مدينة', 80), ('المغرب', 77), ('المغاربة', 72), ('يوم', 72)]

Most Common Bigrams:
[(('التجمع', 'الوطني'), 412), (('الوطني', 'للأحرار'), 403), (('عزيز', 'أخنوش'), 222), (('حزب', 'التجمع'), 206), (('للأحرار', 'National'), 141), (('National', 'des'), 141), (('des', 'Indépendants'), 141), (('partirni', 'Instagram'), 115), (('Instagram', 'photos'), 115), (('photos', 'and'), 115), (('and', 'videos'), 115), (('RNI', 'التجمع'), 107), (('للأحرار', 'partirni'), 107), (('الفيدرالية', 'الوطنية'), 105), (('الوطنية', 'للفنانين'), 86), (('للفنانين', 'التجمعين'), 85), (('يوم', 'مدينة'), 69), (('لحزب', 'التجمع'), 65), (('برنامج', 'الأحرار'), 63), (('البرنامج', 'الانتخابي'), 50), (('الانتخابي', 'لحزب'), 50), (('قصة', 'عبد'), 43), (('الحملة', 'الانتخابية'), 38), (('خلاصات', 'يوم'), 35), (('فيديو', 'الفيدرالية'), 32), (('رمز', 'الحمامة'), 30), (('عبد', 'الحق'), 29), (('الحق', 'القباب'), 29), (('قطاع', 'الصحة'), 27), (('برنامج', 'الكلمة'), 27)]

4. looking up keywords to find the specific posts and determine the type of centent¶

In [132]:
# example keyword 'الملك'
Keytest = RNI_keywords
Keytest['indexes'] = Keytest['ad_creative_bodies'].str.find('الملك')
Keytest[Keytest['indexes']>0]
Out[132]:
ad_creative_bodies ad_creative_link_titles indexes
3 الرباط، مجلس النواب \n🙏 أكد رئيس الحكومة، عزيز... أخنوش: وضعية المرأة عرفت في عهد جلالة الملك ثو... 108.0
5 نوّه محي الدين حجاج، عضو المكتب السياسي للتجمع... محي الدين حجاج ينوه بمجهودات رئيس الحكومة في م... 263.0
9 تشرع الحكومة، تنفيذا للتوجيهات الملكية السامية... NaN 31.0
30 أشاد محي الدين حجاج، عضو المكتب السياسي للتجمع... حجاج: الأحرار هو الحزب الذي نقل ملف الأمازيغية... 195.0
63 أبرز محمد أوجار، عضو المكتب السياسي لحزب التجم... NaN 198.0
... ... ... ...
5547 تصريح عزيز أخنوش عقب الحفل الذي ترأسه جلالة ال... NaN 44.0
5564 ترأس صاحب الجلالة الملك محمد السادس، نصره الله... حزب التجمع الوطني للأحرار -Rassemblement Natio... 18.0
5565 ترأس صاحب الجلالة الملك محمد السادس، نصره الله... RNI - التجمع الوطني للأحرار (@partirni) • Inst... 18.0
5577 أصدر صاحب الجلالة الملك محمد السادس، نصره الله... حزب التجمع الوطني للأحرار -Rassemblement Natio... 18.0
5965 أصدر صاحب الجلالة الملك محمد السادس، نصره الله... حزب التجمع الوطني للأحرار -Rassemblement Natio... 18.0

343 rows × 3 columns

II. Ads details¶

1. Ads durations¶

The ad duration signifies how long the ad was online (in days).

In [111]:
RNI_clean['ad_duration'] = RNI_clean['ad_delivery_stop_time'] - RNI_clean['ad_delivery_start_time']
RNI_duration = RNI_clean['ad_duration'].value_counts()
RNI_duration.to_csv('Output/RNI_duration.csv', encoding='utf-8')
RNI_duration.to_frame().head()
Out[111]:
count
ad_duration
2 days 1347
3 days 943
1 days 898
4 days 688
5 days 489

2. Prefered platforms¶

Meta offers a multitude of platform options that advertisers can use to publish sponsored content.

Meta Audience Network option extends Meta's people-based advertising beyond the Facebook app. With Audience Network, publishers can make money by showing ads from Meta advertisers in their apps.

In [112]:
RNI_platforms = RNI_clean['publisher_platforms'].value_counts()
RNI_platforms.to_csv('Output/RNI_platforms.csv', encoding='utf-8')
RNI_platforms.to_frame()
Out[112]:
count
publisher_platforms
facebook 3290
instagram 1472
facebook,instagram 1204
facebook,audience_network 13
facebook,instagram,audience_network 5
facebook,audience_network,messenger 1
facebook,messenger 1

3. Ads languages¶

In [113]:
RNI_languages = RNI_clean['languages'].value_counts()
RNI_languages.to_csv('Output/RNI_languages.csv', encoding='utf-8')
RNI_languages.to_frame()
Out[113]:
count
languages
ar 5340
fr 518
en 17
tz 6

4. Overall budget ranges¶

Amount spent is the estimated total amount of money spent on an ad during its schedule.

In [114]:
RNI_spendings = RNI_clean['spend'].value_counts()
RNI_spendings.to_csv('Output/RNI_spendings.csv', encoding='utf-8')
RNI_spendings.to_frame()
Out[114]:
count
spend
lower_bound: 0, upper_bound: 99 4933
lower_bound: 100, upper_bound: 199 572
lower_bound: 200, upper_bound: 299 190
lower_bound: 300, upper_bound: 399 94
lower_bound: 400, upper_bound: 499 74
lower_bound: 500, upper_bound: 599 30
lower_bound: 1000, upper_bound: 1499 29
lower_bound: 600, upper_bound: 699 19
lower_bound: 800, upper_bound: 899 15
lower_bound: 700, upper_bound: 799 11
lower_bound: 1500, upper_bound: 1999 8
lower_bound: 2000, upper_bound: 2499 5
lower_bound: 900, upper_bound: 999 4
lower_bound: 3000, upper_bound: 3499 1
lower_bound: 2500, upper_bound: 2999 1

5. Potential reach¶

Estimated Audience Size generally estimates how many Accounts Center accounts meet the targeting and ad placement criteria that advertisers select while creating an ad. Estimates aren't designed to match population, census estimates or other sources and may differ depending on factors.

In [115]:
RNI_audience = RNI_clean['estimated_audience_size'].value_counts()
RNI_audience.to_csv('Output/RNI_audience.csv', encoding='utf-8')
RNI_audience.to_frame()
Out[115]:
count
estimated_audience_size
lower_bound: 1000001 4146
lower_bound: 100001, upper_bound: 500000 1043
lower_bound: 500001, upper_bound: 1000000 675
lower_bound: 10001, upper_bound: 50000 55
lower_bound: 50001, upper_bound: 100000 54
lower_bound: 100, upper_bound: 1000 8
lower_bound: 1001, upper_bound: 5000 5

Impressions are the number of times an ad was on a screen. May include multiple views by the same people.

In [116]:
RNI_impressions = RNI_clean['impressions'].value_counts()
RNI_impressions.to_csv('Output/RNI_impressions.csv', encoding='utf-8')
RNI_impressions.to_frame()
Out[116]:
count
impressions
lower_bound: 10000, upper_bound: 14999 454
lower_bound: 15000, upper_bound: 19999 369
lower_bound: 50000, upper_bound: 59999 327
lower_bound: 20000, upper_bound: 24999 298
lower_bound: 0, upper_bound: 999 292
lower_bound: 60000, upper_bound: 69999 265
lower_bound: 100000, upper_bound: 124999 253
lower_bound: 25000, upper_bound: 29999 253
lower_bound: 70000, upper_bound: 79999 223
lower_bound: 40000, upper_bound: 44999 219
lower_bound: 30000, upper_bound: 34999 215
lower_bound: 35000, upper_bound: 39999 207
lower_bound: 125000, upper_bound: 149999 192
lower_bound: 45000, upper_bound: 49999 184
lower_bound: 150000, upper_bound: 174999 166
lower_bound: 200000, upper_bound: 249999 166
lower_bound: 80000, upper_bound: 89999 165
lower_bound: 1000, upper_bound: 1999 133
lower_bound: 175000, upper_bound: 199999 129
lower_bound: 90000, upper_bound: 99999 118
lower_bound: 6000, upper_bound: 6999 116
lower_bound: 4000, upper_bound: 4999 112
lower_bound: 7000, upper_bound: 7999 110
lower_bound: 250000, upper_bound: 299999 110
lower_bound: 3000, upper_bound: 3999 109
lower_bound: 5000, upper_bound: 5999 102
lower_bound: 2000, upper_bound: 2999 100
lower_bound: 8000, upper_bound: 8999 96
lower_bound: 9000, upper_bound: 9999 74
lower_bound: 350000, upper_bound: 399999 68
lower_bound: 1000000 67
lower_bound: 300000, upper_bound: 349999 62
lower_bound: 500000, upper_bound: 599999 55
lower_bound: 400000, upper_bound: 449999 50
lower_bound: 450000, upper_bound: 499999 39
lower_bound: 600000, upper_bound: 699999 32
lower_bound: 700000, upper_bound: 799999 31
lower_bound: 800000, upper_bound: 899999 17
lower_bound: 900000, upper_bound: 999999 8

6. Calculating budget and impressions per month¶

Since the data is given in the form of ranges and not specific values, we will calculate both the upper and lower bounds for the budget spent and the number of impressions per each month.

In [106]:
RNI_budget_impressions = RNI_clean[['ad_delivery_start_time', 'impressions','spend']]
# spliting the spending data to different columns
RNI_budget_impressions[['lower_spd','upper_spd']] = RNI_budget_impressions.spend.str.split(',', expand = True)
RNI_budget_impressions[['txt','lower_spend']] = RNI_budget_impressions.lower_spd.str.split(':', expand = True)
RNI_budget_impressions[['txt','upper_spend']] = RNI_budget_impressions.upper_spd.str.split(':', expand = True)
# spliting the spending data to different columns
RNI_budget_impressions[['lower_imp','upper_imp']] = RNI_budget_impressions.impressions.str.split(',', expand = True)
RNI_budget_impressions[['txt','lower_impressions']] = RNI_budget_impressions.lower_imp.str.split(':', expand = True)
RNI_budget_impressions[['txt','upper_impressions']] = RNI_budget_impressions.upper_imp.str.split(':', expand = True)
# removing extra columns
RNI_budget_impressions = RNI_budget_impressions.drop(columns = ['impressions','spend','txt','lower_spd','upper_spd','lower_imp','upper_imp'])
# transforming the new columns into the correct data type
RNI_budget_impressions[['lower_spend','upper_spend','lower_impressions','upper_impressions']] = RNI_budget_impressions[['lower_spend','upper_spend','lower_impressions','upper_impressions']].apply(pd.to_numeric)
# replacing any NaN values in the upper_impressions column and changing the data type to int64
RNI_budget_impressions['upper_impressions'].fillna(1000000, inplace=True)
RNI_budget_impressions['upper_impressions'] = RNI_budget_impressions['upper_impressions'].astype('int64')
# adding new column with only the month and year
RNI_budget_impressions["ad_creation_month"] = RNI_budget_impressions['ad_delivery_start_time'].dt.strftime("%Y-%m")
RNI_budget_impressions = RNI_budget_impressions.drop(columns = ['ad_delivery_start_time'])
# Summing up the budget and impressions for each month
RNI_month = RNI_budget_impressions.groupby(['ad_creation_month'], as_index=False).sum(['lower_spend', 'upper_spend', 'lower_impressions', 'upper_impressions'])
RNI_month.to_csv('Output/RNI_month.csv', encoding='utf-8')
RNI_month
Out[106]:
ad_creation_month lower_spend upper_spend lower_impressions upper_impressions
0 2021-03 4700 25498 10683000 12435800
1 2021-04 10400 59207 29151000 33846511
2 2021-05 13200 86282 33629000 39022287
3 2021-06 34000 122819 70415000 80176135
4 2021-07 4200 21426 18074000 20778829
5 2021-08 88100 185608 138445000 157104137
6 2021-09 73100 110364 45328000 52095671
7 2021-11 200 299 250000 299999
8 2021-12 1600 3085 5700000 6506985
9 2022-01 300 9705 6848000 7978905
10 2022-02 4900 17473 14655000 17166873
11 2022-03 700 25747 12712000 15102747
12 2022-04 100 10495 4070000 4864895
13 2022-05 100 15445 7802000 9219845
14 2022-06 800 32579 15839000 18694679
15 2022-07 2900 31214 18524000 21498715
16 2022-08 0 1386 684000 801986
17 2022-09 16900 65410 52573000 61729510
18 2022-10 1000 6049 7252000 8552949
19 2022-11 0 5544 1181000 1438944
20 2023-02 200 5447 4719000 5573947
21 2023-03 0 13068 6302000 7454868
22 2023-04 200 1388 1960000 2284988
23 2023-05 400 11983 13472000 16000883
24 2023-06 0 99 250000 299999
25 2023-07 1200 1794 2400000 2749994

7. Extracting the data of the top 20 spenders on ads about social issues, elections or politics.¶

In [74]:
General_top20.to_csv('Output/General_top20.csv', encoding='utf-8')
General_top20
Out[74]:
Page name Amount spent (USD) Number of ads in Library
0 حزب التجمع الوطني للأحرار -Rassemblement Natio... 425662 5896
1 La Nouvelle Tribune 315542 1149
2 الحكومة المغربية 94219 1847
3 Parti de l'Istiqlal - حزب الاستقلال 40549 355
4 Ministère de l’Industrie et du Commerce 31866 279
5 MJCC وزارة الشباب والثقافة والتواصل 29785 2544
6 اليونيسف بالعربية - UNICEF in Arabic 29467 162
7 Ministère de l’Equipement et de l’Eau - وزارة ... 25928 249
8 حزب الأصالة والمعاصرة PAM 20019 154
9 الفيدرالية الوطنية للشبيبة التجمعية 15931 101
10 Fatima Ezzahra El Mansouri 14406 147
11 HESPRESS 13436 262
12 Aziz Akhannouch - عزيز أخنوش 12985 59
13 المجلس الاقتصادي والاجتماعي والبيئي CESE Maroc 11968 128
14 UNICEF (FR) 11853 147
15 Rue20.Com 9515 1156
16 Fatim-Zahra Ammor - فاطمة الزهراء عمور 8167 76
17 Rabat, Capital of African Culture 2022 8119 412
18 PPS Maroc حزب التقدم و الإشتراكية 7477 288
19 Houcine Nasrollah الحسين نصر الله 7107 86

Summary¶

From all the data above, we can draw the following conclusions:

  • The majority of ad campaigns stay up for only a short period of time (less than a week).
  • Facebook is the preferred way of publishing sponsored content.
  • Arabic is the dominating language used in ads.
  • When it comes to spendings, the majority of ads fall in the 0 - 99 dollars range. This means that the RNI has a high quantity while maintaining a low cost per ad approach to advertising on social media.
  • Most of RNI's ads' estimated audience size fall within the "over 1 million" category. This signifies that their targeting strategy is to aim for a more broad and diverse demographic.
  • The majority of impressions for ads falls on the low end (less than 100000 impressions for over 4500 of the ads). This can be explained by the short duration for most ads.
  • The large ads budget and number of impressions during summer 2021 is due to the general elections that were held in Morocco on 8 September 2021, one which the RNI led by Aziz Akhannouch won the most seats (102) in the House of Representatives. One topic to be further explored is how did the RNI digital marketing strategy contribute to their success in 2021.
  • After observing the number of impressions per month alongside the amount of money spent, we can deduce the existence of a correlation between the 2. It may be possible to build a predictive model where we can determine the potential impressions the RNI could get based on the amount of money they spend and the time of the year.