Mathew K Analytics

Lesson 56 · Social Media Content Analytics

Turning Social Media Data into Insights

In this lesson, we will learn how to transform raw social media metrics into useful business and content insights. You will see how content creators and…

⬇ 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

Turning Social Media Data into Insights#

  • In this lesson, we will learn how to transform raw social media metrics into useful business and content insights.
  • You will see how content creators and brands can discover what works, when to post, and how to improve, using real engagement data.
  • We will walk through hands-on examples using public datasets representing trending videos, social media posts, and time series engagement.
  • By the end of the lesson, you will be able to identify high-performing content and understand key patterns in social media analytics.
import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings('ignore')

Core Social Media Analytics Concepts#

  • Social media datasets usually track content (videos, posts) alongside engagement metrics like views, likes, comments, shares, and watch time.
  • Views measure reach, while likes and comments reflect deeper engagement.
  • Click-through rate (CTR) and watch time help show audience interest and quality.
  • Engagement rate helps compare performance across different posts and sizes.
  • It is important not to focus on one metric only or to confuse absolute vs. relative performance.
  • Beginners often ignore audience size, or look at ratios like likes per view without context.
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:
    df_trend = fetch_yt_trending()
    print('Live trending data:', df_trend.shape)
except Exception as e:
    print(f'Falling back to synthetic: {e}')
    np.random.seed(42)
    n = 1000
    df_trend = 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_trend.shape)

print(df_trend.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  3685258  499435          29065  
1           1  3587124   41763           3267  
2          24   786247   16975            412  
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.shape)
print(df_content.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
print('Average view count on trending videos:', df_trend['views'].mean())
print('Typical trending video view distribution:')
print(df_trend['views'].describe())
Average view count on trending videos: 301252.59798994975
Typical trending video view distribution:
count    1.990000e+02
mean     3.012526e+05
std      6.376060e+05
min      1.213700e+04
25%      5.778550e+04
50%      9.927600e+04
75%      2.387260e+05
max      3.873711e+06
Name: views, dtype: float64
df_content['engagement_rate'] = ((df_content['likes'] + df_content['comments'] + df_content['shares']) / df_content['views']) * 100
print(df_content[['platform','views','likes','comments','shares','engagement_rate']].head(3))
    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
platform_eng = df_content.groupby('platform')['engagement_rate'].mean().sort_values(ascending=False)
print(platform_eng)
platform
Instagram    13.044316
TikTok       12.690745
YouTube      12.163010
Name: engagement_rate, dtype: float64
top_trending = df_trend.sort_values('views', ascending=False).head(5)
print('Top 5 trending videos by views:')
print(top_trending[['title', 'channel_title', 'views', 'likes', 'comment_count']])
Top 5 trending videos by views:
                                                title     channel_title  \
3   IShowSpeed - World Cup (Champions) [Official M...        IShowSpeed   
0   Ariana Grande - hate that i made you love me (...  ArianaGrandeVevo   
1            The End of Oak Street | Official Trailer      Warner Bros.   
6                        MEOVV(미야오) - ‘DDI RO RI’ M/V     THEBLACKLABEL   
11            1000 VS 1000 Player Minecraft Civil War        FlameFrags   

      views   likes  comment_count  
3   3873711  621762          52459  
0   3685258  499435          29065  
1   3587124   41763           3267  
6   3144640       0           9951  
11  2449964  130668          25220  
median_eng = df_content.groupby('platform')['engagement_rate'].median()
for plat, med in median_eng.items():
    print(f'Platform: {plat}, Median engagement rate: {med:.2f}%')
Platform: Instagram, Median engagement rate: 12.79%
Platform: TikTok, Median engagement rate: 12.90%
Platform: YouTube, Median engagement rate: 12.01%
high_eng = df_content[df_content['engagement_rate'] > 40]
print(f'Number of posts with engagement rate above 40%: {len(high_eng)}')
print(high_eng[['platform', 'views', 'likes', 'comments', 'shares', 'engagement_rate']].head(3))
Number of posts with engagement rate above 40%: 0
Empty DataFrame
Columns: [platform, views, likes, comments, shares, engagement_rate]
Index: []
import matplotlib.pyplot as plt
plt.figure(figsize=(7,4))
plt.scatter(df_content['views'], df_content['likes'], alpha=0.5)
plt.xlabel('Views')
plt.ylabel('Likes')
plt.title('Likes vs. Views for Social Media Posts')
plt.grid(True)
plt.tight_layout()
plt.show()
No description has been provided for this image
df_content['comment_rate'] = (df_content['comments'] / df_content['views']) * 100
mean_comment = df_content.groupby('platform')['comment_rate'].mean()
print('Average comment rate (%) by platform:')
print(mean_comment.round(2))
Average comment rate (%) by platform:
platform
Instagram    2.52
TikTok       2.49
YouTube      2.60
Name: comment_rate, dtype: float64
# Here we define a viral post as having >20,000 views and >25% engagement
viral = df_content[(df_content['views'] > 20000) & (df_content['engagement_rate'] > 25)]
print(f'Number of viral posts identified: {len(viral)}')
print(viral[['platform', 'views', 'engagement_rate']].head())
Number of viral posts identified: 0
Empty DataFrame
Columns: [platform, views, engagement_rate]
Index: []
df_content['hour'] = df_content['date'].dt.hour
hourly_eng = df_content.groupby('hour')['engagement_rate'].mean().sort_values(ascending=False)
best_hour = hourly_eng.idxmax()
print('Average engagement rate by posting hour:')
print(hourly_eng)
print(f'Best posting hour identified: {best_hour}:00')
Average engagement rate by posting hour:
hour
12    13.243221
0     12.631587
18    12.589461
6     11.975891
Name: engagement_rate, dtype: float64
Best posting hour identified: 12:00
leaderboard = df_content.copy()
leaderboard['score'] = leaderboard['engagement_rate']*0.7 + (leaderboard['views']/10000)*0.3
top_content = leaderboard.sort_values('score', ascending=False).head(5)
print('Top 5 posts by combined engagement and reach:')
print(top_content[['post_id', 'platform', 'views', 'engagement_rate', 'score']])
Top 5 posts by combined engagement and reach:
     post_id   platform  views  engagement_rate      score
351      352  Instagram  74643        21.781011  17.485998
87        88     TikTok  82898        20.698931  16.976192
276      277  Instagram  96701        19.929473  16.851661
192      193  Instagram  97604        19.882382  16.845787
392      393     TikTok  99813        19.658762  16.755523
df_broken = df_content.copy()
df_broken.loc[5:8, 'likes'] = np.nan
missing_count = df_broken['likes'].isnull().sum()
print(f'Liking values missing for {missing_count} posts.')
df_broken['likes_filled'] = df_broken['likes'].fillna(df_broken['likes'].median())
print('Missing likes replaced with median value. Example:')
print(df_broken[['likes', 'likes_filled']].iloc[5:10])
Liking values missing for 4 posts.
Missing likes replaced with median value. Example:
    likes  likes_filled
5     NaN        3619.0
6     NaN        3619.0
7     NaN        3619.0
8     NaN        3619.0
9  2567.0        2567.0
bad_agg = df_content.groupby('platform')[['likes', 'comments', 'shares']].sum().sum(axis=1)
print('Sum of engagement across platforms (incorrect):')
print(bad_agg)
good_agg = df_content.groupby('platform').apply(lambda x: ((x['likes'] + x['comments'] + x['shares']) / x['views']).sum())
print('Total correct engagement ratio by platform:')
print(good_agg)
Sum of engagement across platforms (incorrect):
platform
Instagram    1109655
TikTok       1001335
YouTube      1129232
dtype: int64
Total correct engagement ratio by platform:
platform
Instagram    21.131792
TikTok       19.416840
YouTube      22.501569
dtype: float64
small = df_content[df_content['views'] < 300]
print('Posts with very few views and unusual engagement rates:')
print(small[['views', 'likes', 'engagement_rate']].head())
Posts with very few views and unusual engagement rates:
Empty DataFrame
Columns: [views, likes, engagement_rate]
Index: []
category_avg_views = df_trend.groupby('category_id')['views'].mean()
print('Average trending video views per category:')
print(category_avg_views.round().astype(int))
Average trending video views per category:
category_id
1     1896410
10     642188
17     184422
20     232385
22      82685
24     241429
28     294708
Name: views, dtype: int64
bench = df_content.groupby('platform')['views'].median()
for plat, mid in bench.items():
    print(f'Median views per post on {plat}: {mid}')
Median views per post on Instagram: 52934.0
Median views per post on TikTok: 53021.0
Median views per post on YouTube: 52090.0
df_content['quartile'] = pd.qcut(df_content['engagement_rate'], 4, labels=['Q1','Q2','Q3','Q4'])
segment_counts = df_content.groupby(['platform', 'quartile']).size().unstack().fillna(0).astype(int)
print('Count of posts per engagement rate quartile, by platform:')
print(segment_counts)
Count of posts per engagement rate quartile, by platform:
quartile   Q1  Q2  Q3  Q4
platform                 
Instagram  33  46  36  47
TikTok     42  32  42  37
YouTube    50  47  47  41
from matplotlib.dates import DateFormatter
daily_eng = df_content.groupby(df_content['date'].dt.date)['engagement_rate'].mean()
plt.figure(figsize=(10,4))
plt.plot(daily_eng.index, daily_eng.values)
plt.title('Engagement Rate Trend Over Time')
plt.xlabel('Date')
plt.ylabel('Average Engagement Rate (%)')
plt.gca().xaxis.set_major_formatter(DateFormatter('%b-%d'))
plt.xticks(rotation=45)
plt.grid(True)
plt.tight_layout()
plt.show()
No description has been provided for this image
def consistent_engagement(df):
    return ((df['likes'].fillna(0) + df['comments'].fillna(0) + df['shares'].fillna(0)) / df['views']) * 100
df_content['cons_engage'] = consistent_engagement(df_content)
print(df_content[['engagement_rate', 'cons_engage']].head())
   engagement_rate  cons_engage
0        17.143756    17.143756
1        14.166667    14.166667
2        10.366615    10.366615
3         7.187284     7.187284
4        10.636292    10.636292
benchmark = df_content.groupby('platform')['engagement_rate'].mean()
top_platform = benchmark.idxmax()
print(f'Your top-performing platform for engagement is: {top_platform}')
if top_platform == 'TikTok':
    print('Consider making more TikTok posts when aiming for high engagement.')
elif top_platform == 'Instagram':
    print('Focus on Instagram strategies, such as Stories or Reels, for boosting engagement.')
elif top_platform == 'YouTube':
    print('Emphasize YouTube Shorts, Community posts, or longer-form videos for more interaction.')
Your top-performing platform for engagement is: Instagram
Focus on Instagram strategies, such as Stories or Reels, for boosting engagement.
top_post = df_content.sort_values('engagement_rate', ascending=False).iloc[0]
print('Top post for engagement:')
print(top_post[['platform', 'date', 'views', 'likes', 'comments', 'shares', 'engagement_rate']])
recommendation = f'Post more on {top_post["platform"]} at {top_post["date"].hour}:00 for better engagement!'
print('Recommendation:', recommendation)
Top post for engagement:
platform                     Instagram
date               2023-03-29 18:00:00
views                            74643
likes                            10653
comments                          3569
shares                            2036
engagement_rate              21.781011
Name: 351, dtype: object
Recommendation: Post more on Instagram at 18:00 for better engagement!

If you want more lessons like this, please like the YouTube video!#

Subscribe to the channel so you do not miss the next lesson on social data analytics!#

Found this useful?

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