Mathew K Analytics

Lesson 4 · Social Media Content Analytics

Social Media Analytics Workflow Using Python

Learn to solve real social media analytics problems step by step. Discover which metrics matter for content creators and businesses. Build insights from…

⬇ Download notebookOpen in Colab ↗

📓 Full notebook

Download .ipynb

Social Media Analytics Workflow Using Python#

  • Learn to solve real social media analytics problems step by step.
  • Discover which metrics matter for content creators and businesses.
  • Build insights from engagement metrics, trends, and content performance.
  • By the end, you will make data-driven recommendations to optimize your content strategy.
import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings('ignore')

Key Social Media Analytics Concepts and Metrics#

  • Social media datasets contain posts, videos, engagement, and platform data.
  • Metrics like views, likes, comments, click-through rate, and watch time reflect audience behavior.
  • Engagement rate shows how often viewers interact with your content.
  • Beginners often mistake high views for success or forget relative engagement.
  • Correct interpretation of ratios and groupings is critical for insight.
# Load core engagement dataset for multi-platform analysis
np.random.seed(42)
n_posts = 500
views = np.random.randint(100, 100000, n_posts)
df_content = 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_content.head(3))
   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
# Load YouTube-specific trending videos dataset
try:
    from pathlib import Path
    import os, pickle
    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)
    df_trending = fetch_yt_trending()
    print('Live trending data:', df_trending.shape)
except Exception as e:
    print(f'Falling back to synthetic: {e}')
    np.random.seed(42)
    n = 1000
    df_trending = 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:', df_trending.shape)
print(df_trending.head(3))
Falling back to synthetic: <HttpError 403 when requesting https://youtube.googleapis.com/youtube/v3/videos?part=snippet%2Cstatistics&chart=mostPopular&regionCode=US&maxResults=50&key=YOUR_GOOGLE_API_KEY&alt=json returned "The request cannot be completed because you have exceeded your <a href="/youtube/v3/getting-started#quota">quota</a>.". Details: "[{'message': 'The request cannot be completed because you have exceeded your <a href="/youtube/v3/getting-started#quota">quota</a>.', 'domain': 'youtube.quota', 'reason': 'quotaExceeded'}]">
Synthetic fallback: (1000, 8)
  video_id trending_date          title channel_title  category_id    views  \
0     vid0    2023-01-01  Video Title 0      ChannelC           10  3958242   
1     vid1    2023-01-02  Video Title 1      ChannelA           10  1227060   
2     vid2    2023-01-03  Video Title 2      ChannelC           24  1041519   

    likes  comment_count  
0  144402          18611  
1  160181           4977  
2   33966          18763  

Beginner Example 1: Calculate Engagement Rate by Platform#

  • Engagement rate = (likes + comments + shares) / views
  • This metric shows how actively audiences interact with content.
  • Higher engagement means more loyal or interested viewers.
df_content['engagement_rate'] = ((df_content['likes'] + df_content['comments'] + df_content['shares']) / df_content['views']) * 100
platform_engagement = df_content.groupby('platform')['engagement_rate'].mean().sort_values(ascending=False)
print('Average Engagement Rate by Platform (%):\n', platform_engagement.round(2))
Average Engagement Rate by Platform (%):
 platform
Instagram    13.04
TikTok       12.69
YouTube      12.16
Name: engagement_rate, dtype: float64

Beginner Example 2: Identify Top 5 Most Liked Posts Across Platforms#

  • Sorting by likes helps pinpoint posts that resonate strongly with audiences.
  • This is often a key early discovery step for content improvement.
top5_likes = df_content.sort_values('likes', ascending=False).head(5)
print(top5_likes[['post_id', 'platform', 'likes', 'views', 'engagement_rate']])
     post_id   platform  likes  views  engagement_rate
138      139     TikTok  13839  98906        17.687501
276      277  Instagram  13427  96701        19.929473
192      193  Instagram  13347  97604        19.882382
74        75  Instagram  13323  99399        17.980060
44        45    YouTube  12997  87413        19.149326

Beginner Example 3: Find Most Commented Platform#

  • Comments often indicate deeper engagement or discussion.
  • Which platform sparks the most conversations?
platform_comments = df_content.groupby('platform')['comments'].sum()
most_discussed = platform_comments.idxmax()
print(f'Most discussed platform: {most_discussed} with {platform_comments[most_discussed]:,} comments')
Most discussed platform: YouTube with 246,944 comments

Intermediate Example 1: Analyze Engagement by Posting Hour#

  • When you post matters. Analyze patterns by hour-of-day.
  • Content posted during peak audience times often performs better.
df_content['hour'] = df_content['date'].dt.hour
hourly_engagement = df_content.groupby('hour')['engagement_rate'].mean()
peak_hour = hourly_engagement.idxmax()
print('Average engagement rate by posting hour (%)')
print(hourly_engagement.round(2))
print(f'Best posting hour: {peak_hour}:00 with {hourly_engagement[peak_hour]:.2f}% average engagement')
Average engagement rate by posting hour (%)
hour
0     12.63
6     11.98
12    13.24
18    12.59
Name: engagement_rate, dtype: float64
Best posting hour: 12:00 with 13.24% average engagement

Intermediate Example 2: Compare Top YouTube Category Engagement#

  • Identify which YouTube content categories perform best.
  • This informs content strategy decisions at the topic level.
category_means = df_trending.groupby('category_id')[['views','likes','comment_count']].mean()
category_means['engagement_rate'] = ((category_means['likes'] + category_means['comment_count']) / category_means['views']) * 100
top_cat = category_means['engagement_rate'].idxmax()
print('YouTube category engagement rate (mean, %):')
print(category_means[['engagement_rate']].sort_values('engagement_rate', ascending=False))
print(f'Highest average engagement: Category {top_cat}')
YouTube category engagement rate (mean, %):
             engagement_rate
category_id                 
10                  5.410441
22                  4.989042
24                  4.892867
28                  4.791076
1                   4.707212
2                   4.672833
Highest average engagement: Category 10

Intermediate Example 3: Viral Content Detection by Outlier Engagement#

  • Viral posts tend to have much higher engagement than usual.
  • Let us flag posts with engagement rate well above the dataset average.
er_mean = df_content['engagement_rate'].mean()
er_std = df_content['engagement_rate'].std()
viral_threshold = er_mean + 2 * er_std
viral_posts = df_content[df_content['engagement_rate'] > viral_threshold]
print(f'Number of viral posts: {len(viral_posts)}')
print('Sample viral posts:')
print(viral_posts[['post_id','platform','views','likes','engagement_rate']].head())
Number of viral posts: 4
Sample viral posts:
     post_id   platform  views  likes  engagement_rate
10        11  Instagram  16123   2202        21.205731
351      352  Instagram  74643  10653        21.781011
383      384     TikTok  36731   4965        20.925104
423      424     TikTok  53021   7572        20.882292

Advanced Example 1: Time Series Analysis of Daily Social Media Growth#

  • Understanding how views and engagement change over time reveals content strategy strengths.
  • Let us analyze trends using a simulated daily time series.
np.random.seed(42)
n_days = 365
dates = pd.date_range('2023-01-01', periods=n_days, freq='D')
views = np.random.randint(1000, 50000, n_days)
likes = (views * np.random.uniform(0.03, 0.12, n_days)).astype(int)
df_daily = pd.DataFrame({
    'date': dates,
    'views': views,
    'likes': likes,
    'engagement_rate': np.round(likes / views * 100, 2)
})
rolling_views = df_daily['views'].rolling(7).mean()
rolling_eng = df_daily['engagement_rate'].rolling(7).mean()
print('Recent weekly average views:', rolling_views.tail(1).values[0])
print('Recent weekly average engagement rate:', rolling_eng.tail(1).values[0])
Recent weekly average views: 18696.714285714286
Recent weekly average engagement rate: 7.362857142857142

Advanced Example 2: Audience Segment Insights#

  • Segment your audience by platform and hour to personalize strategies.
  • Segmented insights often unlock growth that is invisible in overall means.
segment = df_content.groupby(['platform','hour'])['engagement_rate'].mean().unstack()
best_segments = segment.max(axis=1).sort_values(ascending=False)
print('Platform/hour with highest engagement:')
print(best_segments.head())
Platform/hour with highest engagement:
platform
Instagram    13.829643
YouTube      13.360360
TikTok       13.114695
dtype: float64

Advanced Example 3: Correcting Engagement Calculations When Data Is Missing#

  • Real-world data may contain missing values in likes, comments, or shares.
  • Never let a missing value turn into skewed aggregate metrics.
# Simulate missing likes and shares for random posts
df_content_missing = df_content.copy()
df_content_missing.loc[df_content_missing.sample(frac=0.05, random_state=42).index, 'likes'] = np.nan
df_content_missing.loc[df_content_missing.sample(frac=0.03, random_state=1).index, 'shares'] = np.nan
# Fill missing with zero
df_content_missing[['likes','shares']] = df_content_missing[['likes','shares']].fillna(0)
df_content_missing['engagement_rate'] = ((df_content_missing['likes'] + df_content_missing['comments'] + df_content_missing['shares']) / df_content_missing['views']) * 100
avg_eng_missing = df_content_missing['engagement_rate'].mean()
print(f'Average engagement rate with missing data handled: {avg_eng_missing:.2f}%')
Average engagement rate with missing data handled: 12.18%

Best Practices: Common Patterns in Social Media Analytics#

  • Benchmark against your past content, not just averages.
  • Always segment by audience or timing to spot hidden trends.
  • Keep metric definitions consistent across teams.
  • Use rolling statistics and outlier detection to monitor growth and viral events.
  • Test content optimizations iteratively.
# Consistent metric benchmarking example (YouTube trending dataset)
latest_date = df_trending['trending_date'].max()
recent = df_trending[df_trending['trending_date'] > latest_date - pd.Timedelta(days=7)]
historical = df_trending[df_trending['trending_date'] <= latest_date - pd.Timedelta(days=7)]
recent_mean = recent['views'].mean()
historical_mean = historical['views'].mean()
change = ((recent_mean - historical_mean) / historical_mean) * 100 if historical_mean > 0 else np.nan
print(f'Average trending video views last 7 days: {recent_mean:.0f}')
print(f'Previous period: {historical_mean:.0f}')
print(f'Change vs previous period: {change:.2f}%')
Average trending video views last 7 days: 2572498
Previous period: 2562694
Change vs previous period: 0.38%

Common Analytics Error Example: Wrongly Aggregated Engagement Data#

  • A common error is to average numerator and denominator before dividing.
  • Always sum or average after calculating ratios per-row.
# Wrong way: Averaging likes and views separately, then dividing
wrong_engagement = (df_content['likes'].mean() + df_content['comments'].mean() + df_content['shares'].mean()) / df_content['views'].mean() * 100
# Right way: Mean of the engagement rate we already calculated
right_engagement = df_content['engagement_rate'].mean()
print(f'Wrongly computed engagement rate: {wrong_engagement:.2f}%')
print(f'Proper engagement rate (mean of per-post): {right_engagement:.2f}%')
Wrongly computed engagement rate: 12.77%
Proper engagement rate (mean of per-post): 12.61%

End-to-End Analytics Task: Make a Content Strategy Recommendation#

  • Let us go from raw data to an actionable recommendation for improving engagement.
  • Identify your best content type and ideal posting hour in one workflow.
summary = df_content.groupby(['platform','hour'])['engagement_rate'].mean().unstack()
best_platform = summary.mean(axis=1).idxmax()
best_hour = summary.loc[best_platform].idxmax()
print(f'Optimal platform for engagement: {best_platform}')
print(f'Best hour to post on {best_platform}: {best_hour}:00')
print(f'Recommendation: Focus content on {best_platform} around {best_hour}:00 hour to maximize audience interaction.')
Optimal platform for engagement: Instagram
Best hour to post on Instagram: 12:00
Recommendation: Focus content on Instagram around 12:00 hour to maximize audience interaction.

Continue Learning: Explore More Analytics and Visualization#

  • Try creating charts of engagement trends over time.
  • Look at platform-specific insights.
  • Check out more examples and tutorials on YouTube for hands-on guidance.

Found this useful?

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