Skip to content

Instantly share code, notes, and snippets.

@tarekahsan709
Last active February 8, 2026 21:36
Show Gist options
  • Select an option

  • Save tarekahsan709/064a6bdd8100197e35b43b0587b7c7d1 to your computer and use it in GitHub Desktop.

Select an option

Save tarekahsan709/064a6bdd8100197e35b43b0587b7c7d1 to your computer and use it in GitHub Desktop.
Meta Add library spent plot of Bangladesh
"""
Facebook Ad Library Analysis: BNP vs Jamaat-e-Islami spending strategy.
Reads the Meta Ad Library CSV for Bangladesh (last 7 days ending Feb 4, 2026),
classifies pages by party affiliation, and produces a dot-strip chart
comparing how each party distributes its ad budget.
"""
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
from pathlib import Path
# ---------------------------------------------------------------------------
# Config
# ---------------------------------------------------------------------------
DATA_DIR = Path("/your_data_path/FacebookAdLibraryReport_2026-02-04_BD_last_7_days")
CSV_PATH = DATA_DIR / "FacebookAdLibraryReport_2026-02-04_BD_last_7_days_advertisers.csv"
OUTPUT_PNG = DATA_DIR / "ad_strategy.png"
OUTPUT_PDF = DATA_DIR / "ad_strategy.pdf"
COLORS = {
"bnp_division": "#3b82f6",
"bnp_other": "#93c5fd",
"jamaat_news": "#ea580c",
"jamaat_leader": "#f59e0b",
"jamaat_party": "#fed7aa",
}
# Page IDs verified through ad content review and public reporting (TBS News).
BNP_PAGE_IDS = {
# Division media cells
101489482880980, # Sylhet Division
108829088795372, # Barishal Division
113809844959141, # Rangpur Division
110235991986292, # Chittagong Division
100998746257599, # Rajshahi Division
103192059367184, # Khulna Division
114137871592874, # Mymensingh Division
105862165769952, # Comilla Division
114868274857729, # Faridpur Division
110377721977993, # Dhaka Division
# Central / official
266326536843123, # BNP Media Cell (central)
353130515348425, # Bangladesh Nationalist Party-BNP
48637583555, # Tarique Rahman
1067017226489655, # Chattogram 11
# Campaign pages
436519556222527, # "Shobar Age Bangladesh" (BNP slogan)
632905399898625, # "Amar Dhan Amar Pran" (BNP paddy symbol)
# Bengali-only named pages
103034728693441, # Dhanmondi Thana BNP
975452825646784, # "We are BNP"
796283883756020, # Sitakunda Municipality BNP
640180602509888, # "Amra BNP Poribar"
}
JAMAAT_PAGE_IDS = {
# Official
646141765463753, # Bangladesh Jamaat-e-Islami
# Leader / campaign
1655241494703764, # Dr. Shafiqur Rahman (Ameer)
246495106022463, # Dr. Shafiqur Rahman supporter group
890218124171294, # "Cholo Eksathe Gori Bangladesh" campaign page
# News pages (verified via ad content review)
480337488505055, # Dhaka News 24 Online
872833149257837, # Dhaka 17 News
# District / local
659361743938323, # Meherpur District
988383787685953, # Health Division
550885641431340, # Fulbaria Upazila
390203917517769, # Mirzapur Upazila
377170452151660, # Kalmakanda
100328701372847, # Bhola-3
109150173882307, # Narayanganj City
560690680469651, # Mainuddin Ahmad
242917998894979, # Dr. Md. Helal Uddin
138774682912184, # Nurul Islam Bulbul
882367374957490, # "Why Jamaat?"
107392944247304, # Chattogram City
579634365236995, # Kishoreganj District
483259565025899, # Dr. Md. Shafiqul Islam Masud
103950325070228, # Charfasson Upazila
545028055353662, # Sher-e-Bangla Nagor
974839482373673, # Noakhali District
995392000320962, # "Adhunik Jamat 24"
801793316357319, # Youth & Sports Division
}
JAMAAT_NEWS_PAGE_IDS = {480337488505055, 872833149257837}
JAMAAT_LEADER_PAGE_IDS = {1655241494703764, 246495106022463, 890218124171294}
BNP_DIVISION_PATTERN = r"BNP Media Cell -|BNP Sylhet"
# ---------------------------------------------------------------------------
# Data loading
# ---------------------------------------------------------------------------
def load_ad_data(path: Path) -> pd.DataFrame:
ads = pd.read_csv(path)
ads["Amount spent (USD)"] = (
ads["Amount spent (USD)"]
.replace("≤100", "50")
.pipe(pd.to_numeric)
)
return ads
# ---------------------------------------------------------------------------
# Classification
# ---------------------------------------------------------------------------
def filter_bnp(ads: pd.DataFrame) -> pd.DataFrame:
by_id = ads["Page ID"].isin(BNP_PAGE_IDS)
by_name = ads["Page name"].str.contains("BNP", case=False, na=False)
by_disclaimer = ads["Disclaimer"].str.contains(
"BNP|Bangladesh Nationalist Party", case=False, na=False
)
is_bnpp = ads["Page name"].str.contains("BNPP", case=False, na=False)
return ads[(by_id | by_name | by_disclaimer) & ~is_bnpp].copy()
def filter_jamaat(ads: pd.DataFrame) -> pd.DataFrame:
by_id = ads["Page ID"].isin(JAMAAT_PAGE_IDS)
by_name = ads["Page name"].str.contains("Jamaat", case=False, na=False)
by_disclaimer = ads["Disclaimer"].str.contains("Jamaat", case=False, na=False)
return ads[by_id | by_name | by_disclaimer].copy()
def classify_jamaat_page(page_id: int) -> str:
if page_id in JAMAAT_NEWS_PAGE_IDS:
return "news"
if page_id in JAMAAT_LEADER_PAGE_IDS:
return "leader"
return "party"
# ---------------------------------------------------------------------------
# Plotting
# ---------------------------------------------------------------------------
def dot_size(spend: float) -> float:
return spend * 0.08 + 30
def build_chart(bnp: pd.DataFrame, jamaat: pd.DataFrame) -> plt.Figure:
plt.rcParams.update({
"font.size": 10,
"font.family": "sans-serif",
"figure.facecolor": "white",
})
bnp_spend = bnp["Amount spent (USD)"].sum()
jamaat_spend = jamaat["Amount spent (USD)"].sum()
bnp_sorted = bnp.sort_values("Amount spent (USD)", ascending=False).reset_index(drop=True)
jamaat_sorted = jamaat.sort_values("Amount spent (USD)", ascending=False).reset_index(drop=True)
fig, ax = plt.subplots(figsize=(11, 5))
y_bnp, y_jamaat = 1.0, 0.0
# BNP dots
bnp_division_ids = set(
bnp[bnp["Page name"].str.contains(BNP_DIVISION_PATTERN, na=False)]["Page ID"]
)
for i, row in bnp_sorted.iterrows():
color = COLORS["bnp_division"] if row["Page ID"] in bnp_division_ids else COLORS["bnp_other"]
ax.scatter(
i, y_bnp,
s=dot_size(row["Amount spent (USD)"]),
c=color, edgecolors="white", linewidth=0.4, alpha=0.85, zorder=3,
)
# Jamaat dots
for i, row in jamaat_sorted.iterrows():
category = classify_jamaat_page(row["Page ID"])
color = COLORS[f"jamaat_{category}"]
ax.scatter(
i, y_jamaat,
s=dot_size(row["Amount spent (USD)"]),
c=color, edgecolors="white", linewidth=0.4, alpha=0.85, zorder=3,
)
# Label the two dominant Jamaat news pages
news_spends = jamaat[jamaat["Page ID"].isin(JAMAAT_NEWS_PAGE_IDS)].sort_values(
"Amount spent (USD)", ascending=False
)
news_label = " + ".join(
f'{row["Page name"]} (${row["Amount spent (USD)"]:,.0f})'
for _, row in news_spends.iterrows()
)
ax.text(
0.5, y_jamaat - 0.28,
f"Two largest: {news_label}",
ha="left", fontsize=7.5, color="#666666",
)
# Axes
ax.set_yticks([y_jamaat, y_bnp])
ax.set_yticklabels(
[
f"Jamaat-e-Islami\n${jamaat_spend:,.0f} · {len(jamaat)} pages",
f"BNP\n${bnp_spend:,.0f} · {len(bnp)} pages",
],
fontsize=11, fontweight="bold",
)
ax.tick_params(axis="y", length=0)
ax.set_ylim(-0.5, 1.5)
ax.set_xticks([])
ax.set_xlabel(
"Each dot = one Facebook page, sorted by spend (largest to smallest)",
fontsize=8.5, color="#888888",
)
for spine in ax.spines.values():
spine.set_visible(False)
# Legend
legend_items = [
Line2D([0], [0], marker="o", color="w", markerfacecolor=COLORS["bnp_division"], markersize=9, label="BNP division cells"),
Line2D([0], [0], marker="o", color="w", markerfacecolor=COLORS["bnp_other"], markersize=7, label="BNP other pages"),
Line2D([0], [0], marker="o", color="w", markerfacecolor=COLORS["jamaat_news"], markersize=9, label="Jamaat news pages"),
Line2D([0], [0], marker="o", color="w", markerfacecolor=COLORS["jamaat_leader"], markersize=8, label="Jamaat leader pages"),
Line2D([0], [0], marker="o", color="w", markerfacecolor=COLORS["jamaat_party"], markersize=6, label="Jamaat party pages"),
]
ax.legend(
handles=legend_items, loc="upper right",
fontsize=8, framealpha=0.95, borderpad=0.8, handletextpad=0.5,
)
fig.text(
0.5, -0.03,
"Source: Meta Ad Library, Bangladesh, 7 days ending Feb 4, 2026",
ha="center", fontsize=7.5, color="#aaaaaa", fontstyle="italic",
)
plt.tight_layout()
return fig
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
def main():
ads = load_ad_data(CSV_PATH)
bnp = filter_bnp(ads)
jamaat = filter_jamaat(ads)
bnp_spend = bnp["Amount spent (USD)"].sum()
jamaat_spend = jamaat["Amount spent (USD)"].sum()
print(f"BNP: ${bnp_spend:,.0f} ({len(bnp)} pages) | Jamaat: ${jamaat_spend:,.0f} ({len(jamaat)} pages)")
fig = build_chart(bnp, jamaat)
fig.savefig(OUTPUT_PNG, dpi=150, bbox_inches="tight", facecolor="white")
fig.savefig(OUTPUT_PDF, bbox_inches="tight", facecolor="white")
print(f"Saved: {OUTPUT_PNG.name} and {OUTPUT_PDF.name}")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment