#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
gen_bsky_pop_graph.py: Generates a graph of the total users on Bluesky in matplotlib using
preformatted data from various sources.
data_array is formatted in the style of "2024-01-31, 5", where the first element is the ISO-8601
date, and where the second element is the total population of Bluesky (existing accounts) at the
end of each date listed (ie. at 23:59).
Note that there may be a variance of about +/- 500 users between trackers and data sources. As the
graph grows with time, there may be a need to amend the visibility or frequency of the ticks,
locators, and data points/labels.
"""
__authors__ = "VintageNebula"
__version__ = "1.4.0"
__date__ = "2024-07-01"
__status__ = "public"
__license__ = "https://creativecommons.org/licenses/by/4.0/"
from datetime import datetime
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from matplotlib.ticker import FuncFormatter, MultipleLocator
data_array = [
# The following portion of data is from "Bluesky and the AT Protocol: Usable Decentralized
# Social Media" by Martin Kleppmann, available at https://arxiv.org/abs/2402.03239. Licensed
# under a Creative Commons Attribution 4.0 International. For more details, please visit:
# https://creativecommons.org/licenses/by/4.0/.
"2022-11-17, 5", "2022-11-18, 10", "2022-11-22, 11", "2022-11-23, 16",
"2022-11-24, 17", "2022-11-25, 18", "2022-11-26, 20", "2022-11-28, 21",
"2022-11-29, 23", "2022-11-30, 32", "2022-12-01, 34", "2022-12-06, 35",
"2022-12-07, 43", "2022-12-11, 44", "2022-12-14, 47", "2022-12-15, 51",
"2022-12-16, 54", "2022-12-17, 55", "2022-12-18, 60", "2022-12-19, 61",
"2022-12-26, 62", "2022-12-28, 63", "2023-01-02, 64", "2023-01-04, 71",
"2023-01-05, 74", "2023-01-08, 75", "2023-01-15, 76", "2023-01-16, 79",
"2023-01-17, 83", "2023-01-18, 90", "2023-01-19, 102", "2023-01-20, 110",
"2023-01-21, 124", "2023-01-22, 129", "2023-01-23, 138", "2023-01-24, 150",
"2023-01-25, 162", "2023-01-26, 171", "2023-01-27, 176", "2023-01-28, 186",
"2023-01-29, 195", "2023-01-30, 196", "2023-01-31, 200", "2023-02-01, 204",
"2023-02-02, 208", "2023-02-03, 228", "2023-02-04, 251", "2023-02-05, 259",
"2023-02-06, 262", "2023-02-07, 271", "2023-02-08, 272", "2023-02-09, 274",
"2023-02-10, 275", "2023-02-11, 277", "2023-02-12, 279", "2023-02-13, 282",
"2023-02-15, 289", "2023-02-16, 311", "2023-02-17, 320", "2023-02-18, 333",
"2023-02-19, 339", "2023-02-20, 342", "2023-02-21, 346", "2023-02-22, 356",
"2023-02-23, 399", "2023-02-24, 432", "2023-02-25, 480", "2023-02-26, 489",
"2023-02-27, 589", "2023-02-28, 657", "2023-03-01, 1037", "2023-03-02, 1387",
"2023-03-03, 1814", "2023-03-04, 2175", "2023-03-05, 2326", "2023-03-06, 2590",
"2023-03-07, 3012", "2023-03-08, 3321", "2023-03-09, 3530", "2023-03-10, 3650",
"2023-03-11, 3738", "2023-03-12, 3817", "2023-03-13, 3958", "2023-03-14, 4040",
"2023-03-15, 4175", "2023-03-16, 4276", "2023-03-17, 4360", "2023-03-18, 4480",
"2023-03-19, 4542", "2023-03-20, 4582", "2023-03-21, 4677", "2023-03-22, 4769",
"2023-03-23, 4808", "2023-03-24, 4835", "2023-03-25, 4962", "2023-03-26, 5118",
"2023-03-27, 5271", "2023-03-28, 5378", "2023-03-29, 5439", "2023-03-30, 5496",
"2023-03-31, 5529", "2023-04-01, 5557", "2023-04-02, 5578", "2023-04-03, 5591",
"2023-04-04, 5605", "2023-04-05, 5689", "2023-04-06, 8638", "2023-04-07, 9721",
"2023-04-08, 10692", "2023-04-09, 11362", "2023-04-10, 12631", "2023-04-11, 16742",
"2023-04-12, 18918", "2023-04-13, 20113", "2023-04-14, 20879", "2023-04-15, 22430",
"2023-04-16, 22915", "2023-04-17, 23467", "2023-04-18, 24767", "2023-04-19, 27032",
"2023-04-20, 29214", "2023-04-21, 31143", "2023-04-22, 32424", "2023-04-23, 33812",
"2023-04-24, 36111", "2023-04-25, 38685", "2023-04-26, 41019", "2023-04-27, 44898",
"2023-04-28, 47857", "2023-04-29, 49482", "2023-04-30, 50889", "2023-05-01, 53005",
"2023-05-02, 55407", "2023-05-03, 57866", "2023-05-04, 60682", "2023-05-05, 62957",
"2023-05-06, 64488", "2023-05-07, 65603", "2023-05-08, 66991", "2023-05-09, 68692",
"2023-05-10, 70405", "2023-05-11, 72024", "2023-05-12, 74092", "2023-05-13, 75707",
"2023-05-14, 76753", "2023-05-15, 78369", "2023-05-16, 79726", "2023-05-17, 81022",
"2023-05-18, 82391", "2023-05-19, 83675", "2023-05-20, 84743", "2023-05-21, 85637",
"2023-05-22, 86684", "2023-05-23, 87947", "2023-05-24, 89557", "2023-05-25, 91497",
"2023-05-26, 93535", "2023-05-27, 95253", "2023-05-28, 96773", "2023-05-29, 98251",
"2023-05-30, 99987", "2023-05-31, 101338", "2023-06-01, 102654", "2023-06-02, 104158",
"2023-06-03, 105294", "2023-06-04, 106333", "2023-06-05, 107579", "2023-06-06, 108848",
"2023-06-07, 110673", "2023-06-08, 112332", "2023-06-09, 114476", "2023-06-10, 116586",
"2023-06-11, 118734", "2023-06-12, 121434", "2023-06-13, 123227", "2023-06-14, 126806",
"2023-06-15, 129687", "2023-06-16, 132298", "2023-06-17, 134480", "2023-06-18, 135731",
"2023-06-19, 137075", "2023-06-20, 138298", "2023-06-21, 141762", "2023-06-22, 151647",
"2023-06-23, 155776", "2023-06-24, 157947", "2023-06-25, 159946", "2023-06-26, 162409",
"2023-06-27, 167285", "2023-06-28, 172214", "2023-06-29, 176846", "2023-06-30, 181867",
"2023-07-01, 200827", "2023-07-03, 238471", "2023-07-04, 251396", "2023-07-05, 259349",
"2023-07-06, 268928", "2023-07-07, 276477", "2023-07-08, 281645", "2023-07-09, 284877",
"2023-07-10, 288558", "2023-07-11, 292067", "2023-07-12, 296804", "2023-07-13, 302133",
"2023-07-14, 307084", "2023-07-15, 310799", "2023-07-16, 314339", "2023-07-17, 318395",
"2023-07-18, 322076", "2023-07-19, 325355", "2023-07-20, 328298", "2023-07-21, 332764",
"2023-07-22, 337710", "2023-07-23, 344879", "2023-07-24, 367953", "2023-07-25, 383160",
"2023-07-26, 391736", "2023-07-27, 401711", "2023-07-28, 411697", "2023-07-29, 420797",
"2023-07-30, 430719", "2023-07-31, 439198", "2023-08-01, 447195", "2023-08-02, 454872",
"2023-08-03, 469502", "2023-08-04, 484070", "2023-08-05, 496008", "2023-08-06, 507967",
"2023-08-07, 521424", "2023-08-08, 531986", "2023-08-09, 539387", "2023-08-10, 546222",
"2023-08-11, 552927", "2023-08-12, 559759", "2023-08-13, 567581", "2023-08-14, 577712",
"2023-08-15, 587345", "2023-08-16, 597619", "2023-08-17, 607273", "2023-08-18, 636040",
"2023-08-19, 654944", "2023-08-20, 666279", "2023-08-21, 677758", "2023-08-22, 688076",
"2023-08-23, 698621", "2023-08-24, 718741", "2023-08-25, 738193", "2023-08-26, 753897",
"2023-08-27, 763237", "2023-08-28, 773600", "2023-08-29, 784464", "2023-08-30, 803706",
"2023-08-31, 822901", "2023-09-01, 841077", "2023-09-02, 850866", "2023-09-03, 860899",
"2023-09-04, 871627", "2023-09-05, 883238", "2023-09-06, 894014", "2023-09-07, 910577",
"2023-09-08, 930818", "2023-09-09, 949826", "2023-09-10, 964057", "2023-09-11, 976140",
"2023-09-12, 996149", "2023-09-13, 1011733", "2023-09-14, 1022749", "2023-09-15, 1032450",
"2023-09-16, 1040703", "2023-09-17, 1049191", "2023-09-18, 1066408", "2023-09-19, 1119588",
"2023-09-20, 1151096", "2023-09-21, 1173578", "2023-09-22, 1192489", "2023-09-23, 1205909",
"2023-09-24, 1218449", "2023-09-25, 1231713", "2023-09-26, 1243960", "2023-09-27, 1255542",
"2023-09-28, 1267029", "2023-09-29, 1288560", "2023-09-30, 1303208", "2023-10-01, 1318732",
"2023-10-02, 1338100", "2023-10-03, 1355993", "2023-10-04, 1371872", "2023-10-05, 1387490",
"2023-10-06, 1404672", "2023-10-07, 1419179", "2023-10-08, 1432002", "2023-10-09, 1446443",
"2023-10-10, 1464752", "2023-10-11, 1482973", "2023-10-12, 1502252", "2023-10-13, 1522928",
"2023-10-14, 1537435", "2023-10-15, 1550713", "2023-10-16, 1564478", "2023-10-17, 1577280",
"2023-10-18, 1612246", "2023-10-19, 1640119", "2023-10-20, 1659872", "2023-10-21, 1674837",
"2023-10-22, 1688403", "2023-10-23, 1703251", "2023-10-24, 1718698", "2023-10-25, 1732165",
"2023-10-26, 1744019", "2023-10-27, 1755474", "2023-10-28, 1766314", "2023-10-29, 1779457",
"2023-10-30, 1793435", "2023-10-31, 1805583", "2023-11-01, 1817179", "2023-11-02, 1830511",
"2023-11-03, 1844526", "2023-11-04, 1857052", "2023-11-05, 1869838", "2023-11-06, 1884462",
"2023-11-07, 1898650", "2023-11-08, 1910725", "2023-11-09, 1928932", "2023-11-10, 1947029",
"2023-11-11, 1966765", "2023-11-12, 1986704", "2023-11-13, 2004950", "2023-11-14, 2015617",
"2023-11-15, 2031851", "2023-11-16, 2048265", "2023-11-17, 2059696", "2023-11-18, 2078645",
"2023-11-19, 2097062", "2023-11-20, 2117378", "2023-11-21, 2137926", "2023-11-22, 2151768",
"2023-11-23, 2166409", "2023-11-24, 2182847", "2023-11-25, 2198920", "2023-11-26, 2212001",
"2023-11-27, 2222298", "2023-11-28, 2245777", "2023-11-29, 2268580", "2023-11-30, 2280482",
"2023-12-01, 2311151", "2023-12-02, 2333666", "2023-12-03, 2358166", "2023-12-04, 2382393",
"2023-12-05, 2392344", "2023-12-06, 2412926", "2023-12-07, 2435814", "2023-12-08, 2458257",
"2023-12-09, 2478499", "2023-12-10, 2487967", "2023-12-11, 2497384", "2023-12-12, 2505149",
"2023-12-13, 2523938", "2023-12-14, 2532879", "2023-12-15, 2547664", "2023-12-16, 2565342",
"2023-12-17, 2585309", "2023-12-18, 2607200", "2023-12-19, 2637705", "2023-12-20, 2654818",
"2023-12-21, 2686305", "2023-12-22, 2704740", "2023-12-23, 2730707", "2023-12-24, 2752527",
"2023-12-25, 2768380", "2023-12-26, 2787860", "2023-12-27, 2801853", "2023-12-28, 2815299",
"2023-12-29, 2831920", "2023-12-30, 2851550", "2023-12-31, 2869398", "2024-01-01, 2889718",
"2024-01-02, 2902171", "2024-01-03, 2915287", "2024-01-04, 2925511", "2024-01-05, 2934089",
"2024-01-06, 2941155", "2024-01-07, 2960571", "2024-01-08, 2990248", "2024-01-09, 3008387",
"2024-01-10, 3018955", "2024-01-11, 3026679", "2024-01-12, 3032916", "2024-01-13, 3038113",
"2024-01-14, 3042987", "2024-01-15, 3047598", "2024-01-16, 3052120", "2024-01-17, 3056502",
"2024-01-18, 3061184", "2024-01-19, 3065586", "2024-01-20, 3069868", "2024-01-21, 3074261",
"2024-01-22, 3078572", "2024-01-23, 3082541", "2024-01-24, 3085972", "2024-01-25, 3089017",
"2024-01-26, 3091891", "2024-01-27, 3094422", "2024-01-28, 3097124", "2024-01-29, 3100356",
"2024-01-30, 3107039", "2024-01-31, 3121850", "2024-02-01, 3140008", "2024-02-02, 3154479",
"2024-02-03, 3168299",
# End of data supplied by Martin Kleppmann.
# Missing data approximated using linear interpolation, Internet Archive.
"2024-02-04, 3187980", "2024-02-05, 3207661", "2024-02-06, 3433226", "2024-02-07, 4088025",
# Beginning of data gathered by jaz.bsky.social
"2024-02-08, 4432203", "2024-02-09, 4570216", "2024-02-10, 4666798", "2024-02-11, 4757049",
"2024-02-12, 4811068", "2024-02-13, 4849070", "2024-02-14, 4858312", "2024-02-15, 4881705",
"2024-02-16, 4899886", "2024-02-17, 4916721", "2024-02-18, 4935407", "2024-02-19, 4952084",
"2024-02-20, 4971871", "2024-02-21, 4991745", "2024-02-22, 5006053", "2024-02-23, 5019561",
"2024-02-24, 5031247", "2024-02-25, 5044627", "2024-02-26, 5056553", "2024-02-27, 5067960",
"2024-02-28, 5078594", "2024-02-29, 5090025", "2024-03-01, 5099009", "2024-03-02, 5107586",
"2024-03-03, 5115734", "2024-03-04, 5123650", "2024-03-05, 5131674", "2024-03-06, 5139313",
"2024-03-07, 5146813", "2024-03-08, 5154932", "2024-03-09, 5163543", "2024-03-10, 5171848",
"2024-03-11, 5179454", "2024-03-12, 5187322", "2024-03-13, 5194968", "2024-03-14, 5203210",
"2024-03-15, 5210243", "2024-03-16, 5216048", "2024-03-17, 5221716", "2024-03-18, 5226977",
"2024-03-19, 5232757", "2024-03-20, 5239602", "2024-03-21, 5245770", "2024-03-22, 5251049",
"2024-03-23, 5257039", "2024-03-24, 5262910", "2024-03-25, 5269076", "2024-03-26, 5274974",
"2024-03-27, 5280278", "2024-03-28, 5286136", "2024-03-29, 5291439", "2024-03-30, 5296232",
"2024-03-31, 5301249", "2024-04-01, 5305827", "2024-04-02, 5311023", "2024-04-03, 5316212",
"2024-04-04, 5321206", "2024-04-05, 5325976", "2024-04-06, 5330463", "2024-04-07, 5372537",
"2024-04-08, 5407180", "2024-04-09, 5421651", "2024-04-10, 5437582", "2024-04-11, 5455018",
"2024-04-12, 5471866", "2024-04-13, 5489161", "2024-04-14, 5502621", "2024-04-15, 5511857",
"2024-04-16, 5525560", "2024-04-17, 5533684", "2024-04-18, 5538455", "2024-04-19, 5542777",
"2024-04-20, 5547038", "2024-04-21, 5551055", "2024-04-22, 5554821", "2024-04-23, 5558553",
"2024-04-24, 5562221", "2024-04-25, 5565822", "2024-04-26, 5569364", "2024-04-27, 5572730",
"2024-04-28, 5576184", "2024-04-29, 5580941", "2024-04-30, 5585312", "2024-05-01, 5589425",
"2024-05-02, 5592993", "2024-05-03, 5596264", "2024-05-04, 5599579", "2024-05-05, 5602813",
"2024-05-06, 5606518", "2024-05-07, 5609813", "2024-05-08, 5613117", "2024-05-09, 5616648",
"2024-05-10, 5619937", "2024-05-11, 5623224", "2024-05-12, 5626214", "2024-05-13, 5629117",
"2024-05-14, 5631856", "2024-05-15, 5634855", "2024-05-16, 5637786", "2024-05-17, 5640573",
"2024-05-18, 5643671", "2024-05-19, 5646611", "2024-05-20, 5649426", "2024-05-21, 5652726",
"2024-05-22, 5656077", "2024-05-23, 5660160", "2024-05-24, 5663423", "2024-05-25, 5666586",
"2024-05-26, 5669773", "2024-05-27, 5674338", "2024-05-28, 5677825", "2024-05-29, 5681494",
"2024-05-30, 5685029", "2024-05-31, 5695026", "2024-06-01, 5698531", "2024-06-02, 5701877",
"2024-06-03, 5705411", "2024-06-04, 5708717", "2024-06-05, 5711829", "2024-06-06, 5714625",
"2024-06-07, 5717341", "2024-06-08, 5804310", "2024-06-09, 5822411", "2024-06-10, 5829823",
"2024-06-11, 5839512", "2024-06-12, 5856822", "2024-06-13, 5880617", "2024-06-14, 5890794",
"2024-06-15, 5910786", "2024-06-16, 5924338", "2024-06-17, 5932536", "2024-06-18, 5938768",
"2024-06-19, 5944088", "2024-06-20, 5948423", "2024-06-21, 5952514", "2024-06-22, 5956543",
# Missing data approximated using linear interpolation.
"2024-06-23, 5960099", "2024-06-24, 5963655", "2024-06-25, 5967211", "2024-06-26, 5970766",
"2024-06-27, 5974322", "2024-06-28, 5977878",
# Continuing data gathered by jaz.bsky.social.
"2024-06-29, 5981434", "2024-06-30, 5984675", "2024-07-01, 5988217"
]
# Parse dates and values from the data array.
date_rng = [datetime.strptime(row.split(',')[0].strip(), "%Y-%m-%d") for row in data_array]
y_values = [int(row.split(',')[1].replace(',', '').strip()) for row in data_array]
# Create an initial plot, set the figure size.
fig, ax = plt.subplots(figsize=(15, 9))
# Plot the main data line.
ax.plot(date_rng, y_values, zorder=2)
ax.fill_between(date_rng, y_values, color="skyblue", alpha=0.4, zorder=1)
# Set the major and minor locators for x-axis; only show odd months (to avoid crowding)
ax.xaxis.set_major_locator(mdates.MonthLocator(bymonth=[1, 3, 5, 7, 9, 11]))
ax.xaxis.set_minor_locator(mdates.MonthLocator())
ax.xaxis.set_major_formatter(mdates.DateFormatter("%Y-%m"))
# Set the major and minor locators for y-axis.
ax.yaxis.set_major_locator(MultipleLocator(1000000)) # Major ticks every 1,000,000
ax.yaxis.set_minor_locator(MultipleLocator(500000)) # Minor ticks every 500,000
# Format y-axis labels with commas (,) for large numbers.
ax.yaxis.set_major_formatter(FuncFormatter(lambda x, _: f'{int(x):,}'))
# Set ticks inside the plot and also on the opposite side, set axis label font size.
ax.tick_params(axis='x', which='both', direction='in', top=True)
ax.tick_params(axis='y', which='both', direction='in', right=True)
ax.tick_params(axis='both', which='major', labelsize=12)
# Make sure y-axis starts at 0.
ax.set_ylim(bottom=0)
# Enable background grid.
ax.grid(True, linestyle=":", linewidth=0.5)
# Hide y-axis offset number (1e6, etc.)
ax.yaxis.offsetText.set_visible(False)
# Highlight and label the first data point of each month.
months = set()
for date, value in zip(date_rng, y_values):
if date.strftime("%Y-%m") not in months:
ax.scatter(date, value, color='C0', marker='o', alpha=1, zorder=3)
ax.annotate(f'{value:,}', (date, value), textcoords="offset points",
xytext=(0, 5), ha='center', fontsize=10) # Smaller font size for annotations
months.add(date.strftime("%Y-%m"))
# Adjust layout and output plot.
plt.tight_layout()
plt.show()