Mathew K Analytics

Lesson 45 · Social Media Content Analytics

Building Automated Content Analytics Systems

In this lesson you will learn how to build an automated analytics workflow for social media content. You will work with real-world datasets that represent…

⬇ Download notebookOpen in Colab ↗

What you'll learn

Data

No separate download needed — the notebook creates or downloads everything it uses.

📓 Full notebook

Download .ipynb

Building Automated Content Analytics Systems#

  • In this lesson you will learn how to build an automated analytics workflow for social media content.
  • You will work with real-world datasets that represent video posts, engagements, and audience feedback.
  • The goal is to help content creators and businesses find what works, spot trends, and make data-driven decisions.
  • By the end, you will generate insights about which content performs best and how to optimize your strategy.
import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings('ignore')

Core Social Media Analytics Concepts#

  • Social media datasets represent posts, videos, or stories on platforms like YouTube, Instagram, and TikTok.
  • Each row is usually a post or video and has columns for views, likes, comments, and shares.
  • Engagement metrics (views, likes, comments) tell us how people interact with content.
  • Watch time and click-through rate (CTR) help us understand deeper user behavior.
  • Beginners often assume more likes or comments always mean better content, but ratios like engagement rate can reveal more.
  • Common mistakes include ignoring outliers, misinterpreting low engagement, or calculating ratios incorrectly.
# Load sample social media posts data
np.random.seed(42)
n_posts = 500
views = np.random.randint(100, 100000, n_posts)
df = pd.DataFrame({
    'post_id': range(1, n_posts+1),
    'platform': np.random.choice(['YouTube','Instagram','TikTok'], n_posts),
    'date': pd.date_range('2023-01-01', periods=n_posts, freq='6h'),
    'views': views,
    'likes': (views * np.random.uniform(0.02, 0.15, n_posts)).astype(int),
    'comments': (views * np.random.uniform(0.001, 0.05, n_posts)).astype(int),
    'shares': (views * np.random.uniform(0.001, 0.03, n_posts)).astype(int)
})
print(df.shape)
print(df.head(3))
(500, 7)
   post_id   platform                date  views  likes  comments  shares
0        1  Instagram 2023-01-01 00:00:00  15895   2356       116     253
1        2  Instagram 2023-01-01 06:00:00    960     94        16      26
2        3  Instagram 2023-01-01 12:00:00  76920   3910      2879    1185
# Beginner Example 1: Calculate engagement rate
df['engagement_rate'] = (df['likes'] + df['comments'] + df['shares']) / df['views'] * 100
print(df[['platform', 'views', 'likes', 'comments', 'shares', 'engagement_rate']].head(5))
    platform  views  likes  comments  shares  engagement_rate
0  Instagram  15895   2356       116     253        17.143756
1  Instagram    960     94        16      26        14.166667
2  Instagram  76920   3910      2879    1185        10.366615
3    YouTube  54986   1827       488    1637         7.187284
4  Instagram   6365    253       261     163        10.636292
# Beginner Example 2: Identify top 5 posts by views
top_views = df.sort_values('views', ascending=False).head(5)
print(top_views[['platform', 'views', 'likes', 'comments', 'shares', 'date']])
      platform  views  likes  comments  shares                date
392     TikTok  99813  12361      4424    2837 2023-04-09 00:00:00
324     TikTok  99622   8409      1888     332 2023-03-23 00:00:00
74   Instagram  99399  13323      1894    2655 2023-01-19 12:00:00
421  Instagram  99257   7373      2990    1434 2023-04-16 06:00:00
138     TikTok  98906  13839      3522     133 2023-02-04 12:00:00
# Beginner Example 3: Most engaging posts overall
most_engaging = df.sort_values('engagement_rate', ascending=False).head(5)
print(most_engaging[['post_id', 'platform', 'views', 'engagement_rate']])
     post_id   platform  views  engagement_rate
351      352  Instagram  74643        21.781011
10        11  Instagram  16123        21.205731
383      384     TikTok  36731        20.925104
423      424     TikTok  53021        20.882292
87        88     TikTok  82898        20.698931
# Intermediate Example 1: Average engagement rate by platform
avg_engagement = df.groupby('platform')['engagement_rate'].mean().sort_values(ascending=False)
print('Average engagement rate by platform:')
print(avg_engagement)
Average engagement rate by platform:
platform
Instagram    13.044316
TikTok       12.690745
YouTube      12.163010
Name: engagement_rate, dtype: float64
# Intermediate Example 2: Engagement trends over time
df['week'] = df['date'].dt.isocalendar().week
weekly_trend = df.groupby(['platform', 'week'])['engagement_rate'].mean().reset_index()
pivot = weekly_trend.pivot(index='week', columns='platform', values='engagement_rate')
pivot.plot(figsize=(10,5), title='Weekly Engagement Rate Trend by Platform')
<Axes: title={'center': 'Weekly Engagement Rate Trend by Platform'}, xlabel='week'>
No description has been provided for this image
# Intermediate Example 3: Find posts with above-average engagement
overall_avg = df['engagement_rate'].mean()
above_avg = df[df['engagement_rate'] > overall_avg]
print(f'Number of posts above global average engagement rate: {len(above_avg)}')
print(above_avg[['platform', 'views', 'engagement_rate']].head(5))
Number of posts above global average engagement rate: 252
     platform  views  engagement_rate
0   Instagram  15895        17.143756
1   Instagram    960        14.166667
10  Instagram  16123        21.205731
12     TikTok  67321        12.626075
13  Instagram  64920        12.929760
# Intermediate Example 4: Best posting time analysis
df['hour'] = df['date'].dt.hour
hourly_engagement = df.groupby('hour')['engagement_rate'].mean()
import matplotlib.pyplot as plt
plt.figure(figsize=(8,4))
hourly_engagement.plot(kind='bar')
plt.title('Average Engagement Rate by Posting Hour')
plt.xlabel('Hour of Day')
plt.ylabel('Engagement Rate (%)')
plt.show()
No description has been provided for this image
# Advanced Example 1: Load and join YouTube trending data for multi-platform analysis
import os, pickle
from pathlib import Path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request

SCOPES = ['https://www.googleapis.com/auth/youtube.readonly']

def get_yt_service():
    api_key = os.environ.get('YOUTUBE_API_KEY')
    if api_key:
        return build('youtube', 'v3', developerKey=api_key)
    if Path('client_secret.json').exists():
        creds = None
        if Path('token_ro.pickle').exists():
            with open('token_ro.pickle', 'rb') as f:
                creds = pickle.load(f)
        if not creds or not creds.valid:
            if creds and creds.expired and creds.refresh_token:
                creds.refresh(Request())
            else:
                flow = InstalledAppFlow.from_client_secrets_file('client_secret.json', SCOPES)
                creds = flow.run_local_server(port=0)
            with open('token_ro.pickle', 'wb') as f:
                pickle.dump(creds, f)
        return build('youtube', 'v3', credentials=creds)
    raise EnvironmentError('Set YOUTUBE_API_KEY or provide client_secret.json')

def fetch_yt_trending(max_results=200, region='US'):
    youtube = get_yt_service()
    records, token = [], None
    while len(records) < max_results:
        resp = youtube.videos().list(
            part='snippet,statistics',
            chart='mostPopular',
            regionCode=region,
            maxResults=min(50, max_results - len(records)),
            pageToken=token
        ).execute()
        for item in resp.get('items', []):
            s = item['snippet']; st = item.get('statistics', {})
            records.append({
                'video_id':      item['id'],
                'trending_date': pd.Timestamp.today().date(),
                'title':         s.get('title', ''),
                'channel_title': s.get('channelTitle', ''),
                'category_id':   s.get('categoryId', ''),
                'views':         int(st.get('viewCount', 0)),
                'likes':         int(st.get('likeCount', 0)),
                'comment_count': int(st.get('commentCount', 0)),
            })
        token = resp.get('nextPageToken')
        if not token: break
    return pd.DataFrame(records)

try:
    yt_df = fetch_yt_trending()
    print('Live trending data:', yt_df.shape)
except Exception as e:
    print(f'Falling back to synthetic: {e}')
    np.random.seed(42)
    n = 1000
    yt_df = pd.DataFrame({
        'video_id':      [f'vid{i}' for i in range(n)],
        'trending_date': pd.date_range('2023-01-01', periods=n, freq='D'),
        'title':         [f'Video Title {i}' for i in range(n)],
        'channel_title': np.random.choice(['ChannelA','ChannelB','ChannelC'], n),
        'category_id':   np.random.choice([1,2,10,22,24,28], n),
        'views':         np.random.randint(10000, 5000000, n),
        'likes':         np.random.randint(100, 200000, n),
        'comment_count': np.random.randint(10, 50000, n),
    })
    print('Synthetic fallback:', yt_df.shape)

print(yt_df.head(3))
Live trending data: (199, 8)
      video_id trending_date  \
0  82-jTNka3uc    2026-06-02   
1  3oB9AxspVow    2026-06-02   
2  l-cyT28MFyk    2026-06-02   

                                               title     channel_title  \
0  Ariana Grande - hate that i made you love me (...  ArianaGrandeVevo   
1           The End of Oak Street | Official Trailer      Warner Bros.   
2        PLAYING HARDCORE MINECRAFT UNTIL WE BEAT IT            Jynxzi   

  category_id    views   likes  comment_count  
0          10  2484247  415393          26707  
1           1  2363780   32366           2734  
2          24   714240   15813            327  
# Advanced Example 2: Detect viral YouTube videos using like-to-view ratio
yt_df['like_view_ratio'] = yt_df['likes'] / yt_df['views']
viral = yt_df[yt_df['like_view_ratio'] > 0.05]
print(f'Total viral videos detected: {len(viral)}')
print(viral[['video_id', 'title', 'views', 'likes', 'like_view_ratio']].head(5))
Total viral videos detected: 100
      video_id                                              title    views  \
0  82-jTNka3uc  Ariana Grande - hate that i made you love me (...  2484247   
3  vrY1THC_NQE  IShowSpeed - World Cup (Champions) [Official M...  2555707   
4  l0hD3KBwoiA  No Peace Amongst the Stars | Warhammer 40,000 ...  2129835   
5  UnFIrVKA9Zc            1000 VS 1000 Player Minecraft Civil War  2149250   
9  iZE1NYmyyh0  Massa Massa Video Song | Peddi | Ram Charan | ...   769019   

    likes  like_view_ratio  
0  415393         0.167211  
3  468058         0.183142  
4  150715         0.070764  
5  124497         0.057926  
9   56999         0.074119  
# Advanced Example 3: Combine platform datasets for unified analysis
social_summary = df.groupby('platform').agg({
    'views': 'sum',
    'likes': 'sum',
    'comments': 'sum',
    'shares': 'sum',
    'engagement_rate': 'mean'
}).reset_index()
yt_summary = yt_df.agg({
    'views': 'sum',
    'likes': 'sum',
    'comment_count': 'sum',
    'like_view_ratio': 'mean'
}).rename({'comment_count':'comments','like_view_ratio':'engagement_rate'})
yt_summary['platform'] = 'YouTube (Trending)'
combined = pd.concat([social_summary, yt_summary.to_frame().T], ignore_index=True)
print(combined[['platform','views','likes','comments','engagement_rate']])
             platform       views      likes  comments engagement_rate
0           Instagram     8546302     761858    216242       13.044316
1              TikTok     7681581     687145    194197       12.690745
2             YouTube     9137173     743813    246944        12.16301
3  YouTube (Trending)  67203153.0  3422599.0  319682.0        0.054133
# Error Example 1: Handle missing engagement values
with_missing = df.copy()
with_missing.loc[::10, 'likes'] = np.nan
print('Rows with missing likes:', with_missing['likes'].isna().sum())
filled = with_missing.fillna({'likes': 0})
print('Preview row where value was set to zero:')
print(filled.iloc[10][['platform','views','likes']])
Rows with missing likes: 50
Preview row where value was set to zero:
platform    Instagram
views           16123
likes             0.0
Name: 10, dtype: object
# Error Example 2: Incorrect metric aggregation (sum vs mean)
grouped_sum = df.groupby('platform')['engagement_rate'].sum()
grouped_mean = df.groupby('platform')['engagement_rate'].mean()
print('Sum of engagement rates (incorrect):')
print(grouped_sum)
print('Mean engagement rates (correct):')
print(grouped_mean)
Sum of engagement rates (incorrect):
platform
Instagram    2113.179174
TikTok       1941.683993
YouTube      2250.156922
Name: engagement_rate, dtype: float64
Mean engagement rates (correct):
platform
Instagram    13.044316
TikTok       12.690745
YouTube      12.163010
Name: engagement_rate, dtype: float64
# Error Example 3: Wrong engagement rate denominator
bad_df = df.copy()
bad_df['bad_engagement'] = (bad_df['likes'] + bad_df['comments'] + bad_df['shares']) / (bad_df['likes'] + 1)
comparison = bad_df[['views','likes','bad_engagement','engagement_rate']].head(3)
print(comparison)
   views  likes  bad_engagement  engagement_rate
0  15895   2356        1.156131        17.143756
1    960     94        1.431579        14.166667
2  76920   3910        2.038865        10.366615
# Error Example 4: Misinterpreting or mis-grouping data
wrong_group = df.groupby('likes')['engagement_rate'].mean()
print('Grouped by likes value, which is rarely useful!')
print(wrong_group.head())
Grouped by likes value, which is rarely useful!
likes
22     5.031447
29    10.784314
51    17.206983
66    11.917563
67     8.880000
Name: engagement_rate, dtype: float64

Best Practices and Analytics Patterns#

  • Always use mean for engagement rates, not sum.
  • Benchmark performance against your own past content as well as industry standards.
  • Try audience segmentation: break out results by platform, content type, and date.
  • Look for long-term trends, seasonality, or sudden spikes in engagement.
  • Use insights from engagement peaks to inform new content ideas.
  • Be consistent: define key metrics once and stick to them in reports.
# Analytics Pattern 1: Content benchmarking report
benchmarks = df.groupby('platform').agg({'engagement_rate':'mean','views':'mean'}).reset_index()
benchmarks['engagement_rate'] = benchmarks['engagement_rate'].round(2)
benchmarks['views'] = benchmarks['views'].round(0).astype(int)
print('Average engagement rate and views by platform:')
print(benchmarks)
Average engagement rate and views by platform:
    platform  engagement_rate  views
0  Instagram            13.04  52755
1     TikTok            12.69  50206
2    YouTube            12.16  49390
# Analytics Pattern 2: Trend and growth analysis using time series
df['date_only'] = df['date'].dt.date
daily_eng = df.groupby('date_only')['engagement_rate'].mean()
import matplotlib.pyplot as plt
plt.figure(figsize=(8,4))
daily_eng.plot()
plt.title('Daily Average Engagement Rate Over Time')
plt.xlabel('Date')
plt.ylabel('Engagement Rate (%)')
plt.tight_layout()
plt.show()
No description has been provided for this image
# Analytics Pattern 3: Audience segmentation example
segmented = df.groupby(['platform','hour'])['engagement_rate'].mean().reset_index()
best_times = segmented.sort_values('engagement_rate', ascending=False).groupby('platform').first().reset_index()
print('Best posting hour by platform:')
print(best_times[['platform','hour','engagement_rate']])
Best posting hour by platform:
    platform  hour  engagement_rate
0  Instagram    12        13.829643
1     TikTok    18        13.114695
2    YouTube    12        13.360360
# Analytics Pattern 4: Outlier/content anomaly detection
q3 = df['engagement_rate'].quantile(0.75)
iqr = q3 - df['engagement_rate'].quantile(0.25)
outliers = df[df['engagement_rate'] > q3 + 1.5*iqr]
print(f'Total outlier (anomalously engaging) posts: {len(outliers)}')
print(outliers[['platform', 'views', 'engagement_rate']].head(3))
Total outlier (anomalously engaging) posts: 0
Empty DataFrame
Columns: [platform, views, engagement_rate]
Index: []
# End-to-End Problem: Recommend best posting strategy from raw data
top_posts = df.sort_values(['engagement_rate'], ascending=False).groupby('platform').head(3)
recommendations = top_posts.groupby('platform').agg({'hour':'median','engagement_rate':'mean'}).reset_index()
recommendations['hour'] = recommendations['hour'].astype(int)
print('Recommended posting hour and content focus per platform:')
print(recommendations)
Recommended posting hour and content focus per platform:
    platform  hour  engagement_rate
0  Instagram    12        21.027045
1     TikTok    18        20.835442
2    YouTube     6        19.449992
# Export dashboard as CSV for wider team use
combined.to_csv('content_analytics_report.csv', index=False)
print('Report written to content_analytics_report.csv')
Report written to content_analytics_report.csv
# Save a visualization as an image file
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(8,4))
hourly_engagement.plot(kind='bar', ax=ax)
plt.title('Average Engagement Rate by Posting Hour')
plt.xlabel('Hour of Day')
plt.ylabel('Engagement Rate (%)')
plt.tight_layout()
plt.savefig('best_time_chart.png')
plt.close()
print('Chart saved as best_time_chart.png')
Chart saved as best_time_chart.png
 

Found this useful?

All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.