Organised Teams Statistics

Analysis of OpenStreetMap contributions from organised teams (including corporations).

Total and Percent of Edits and Contributors from Organised Teams Per Month

code
# Organised teams statistics per month
df = duckdb.sql("""
WITH monthly_total AS (
    SELECT 
        year,
        month,
        CONCAT(year, '-', LPAD(CAST(month as VARCHAR), 2, '0')) as months,
        COUNT(DISTINCT user_name) as total_contributors,
        CAST(SUM(edit_count) as BIGINT) as total_edits
    FROM '../changeset_data/year=*/month=*/*.parquet'
    GROUP BY year, month
),
monthly_organised_team AS (
    SELECT 
        year,
        month,
        CONCAT(year, '-', LPAD(CAST(month as VARCHAR), 2, '0')) as months,
        COUNT(DISTINCT user_name) as team_contributors,
        CAST(SUM(edit_count) as BIGINT) as team_edits
    FROM '../changeset_data/year=*/month=*/*.parquet'
    WHERE organised_team IS NOT NULL
    GROUP BY year, month
)
SELECT 
    mt.months,
    mt.total_contributors as "Total Contributors",
    COALESCE(mot.team_contributors, 0) as "Organised Team Contributors",
    mt.total_edits as "Total Edits",
    COALESCE(mot.team_edits, 0) as "Organised Team Edits",
    ROUND((COALESCE(mot.team_contributors, 0) * 100.0) / mt.total_contributors, 2) as "Percent Contributors from Organised Teams",
    ROUND((COALESCE(mot.team_edits, 0) * 100.0) / mt.total_edits, 2) as "Percent Edits from Organised Teams"
FROM monthly_total mt
LEFT JOIN monthly_organised_team mot ON mt.year = mot.year AND mt.month = mot.month
ORDER BY mt.year, mt.month
""").df()

util.show_figure(
    [
        util.FigureConfig(
            title="Percentage of Edits from Organised Teams",
            label="Edits Percentage",
            x_col="months",
            y_col="Percent Edits from Organised Teams",
            y_unit_hover_template="%",
            query_or_df=df,
        ),
        util.FigureConfig(
            title="Monthly Edits from Organised Teams",
            label="Edits",
            x_col="months",
            y_col="Organised Team Edits",
            query_or_df=df,
        ),
        util.FigureConfig(
            title="Percentage of Contributors from Organised Teams",
            label="Contributors Percentage",
            x_col="months",
            y_col="Percent Contributors from Organised Teams",
            y_unit_hover_template="%",
            query_or_df=df,
        ),
        util.FigureConfig(
            title="Monthly Contributors from Organised Teams",
            label="Contributors",
            x_col="months",
            y_col="Organised Team Contributors",
            query_or_df=df,
        ),
    ]
)

Monthly Contributors, New Contributors, and Edits Per Organised Team (Top 10 Plot)

code
# Top 10 organised teams by contributors
df_top10 = duckdb.sql("""
WITH top_organised_teams AS (
    SELECT organised_team
    FROM (
        SELECT
            organised_team,
            SUM(edit_count) as total_edits
        FROM '../changeset_data/year=*/month=*/*.parquet'
        WHERE organised_team IS NOT NULL
        GROUP BY organised_team
        ORDER BY total_edits DESC
        LIMIT 10
    )
),
user_first_appearance AS (
    SELECT
        user_name,
        year,
        month,
        organised_team,
        ROW_NUMBER() OVER (PARTITION BY user_name ORDER BY year, month) as rn
    FROM (
        SELECT DISTINCT user_name, year, month, organised_team
        FROM '../changeset_data/year=*/month=*/*.parquet'
        WHERE organised_team IN (SELECT organised_team FROM top_organised_teams)
    )
),
first_appearances AS (
    SELECT user_name, year, month, organised_team
    FROM user_first_appearance
    WHERE rn = 1
),
monthly_contributors AS (
    SELECT 
        year,
        month,
        CONCAT(year, '-', LPAD(CAST(month as VARCHAR), 2, '0')) as months,
        organised_team,
        COUNT(DISTINCT user_name) as "Contributors",
        CAST(SUM(edit_count) as BIGINT) as "Edits"
    FROM '../changeset_data/year=*/month=*/*.parquet'
    WHERE organised_team IN (SELECT organised_team FROM top_organised_teams)
    GROUP BY year, month, organised_team
),
monthly_new_contributors AS (
    SELECT
        year,
        month,
        organised_team,
        COUNT(DISTINCT user_name) as "New Contributors"
    FROM first_appearances
    GROUP BY year, month, organised_team
)
SELECT 
    mc.months,
    mc.organised_team,
    mc."Contributors",
    COALESCE(mnc."New Contributors", 0) as "New Contributors",
    mc."Edits"
FROM monthly_contributors mc
LEFT JOIN monthly_new_contributors mnc ON mc.year = mnc.year AND mc.month = mnc.month AND mc.organised_team = mnc.organised_team
ORDER BY mc.year, mc.month, mc.organised_team
""").df()

util.show_figure(
    [
        util.FigureConfig(
            title="Monthly Edits by Top 10 Organised Teams",
            label="Edits",
            x_col="months",
            y_col="Edits",
            group_col="organised_team",
            query_or_df=df_top10,
        ),
        util.FigureConfig(
            title="Monthly Contributors by Top 10 Organised Teams",
            label="Contributors",
            x_col="months",
            y_col="Contributors",
            group_col="organised_team",
            query_or_df=df_top10,
        ),
        util.FigureConfig(
            title="Monthly New Contributors by Top 10 Organised Teams",
            label="New Contributors",
            x_col="months",
            y_col="New Contributors",
            group_col="organised_team",
            query_or_df=df_top10,
        ),
    ]
)

All Organised Teams Table

code
query = """
WITH organised_team_totals AS (
    SELECT
        organised_team as "Organised Team",
        CAST(SUM(edit_count) as BIGINT) as total_edits_all_time,
        CAST(COUNT(DISTINCT user_name) as BIGINT) as total_contributors_all_time
    FROM '../changeset_data/year=*/month=*/*.parquet'
    WHERE organised_team IS NOT NULL
    GROUP BY organised_team
),
yearly_metrics AS (
    SELECT
        d.year,
        d.organised_team as "Organised Team",
        CAST(SUM(d.edit_count) as BIGINT) as "Edits",
        CAST(COUNT(DISTINCT d.user_name) as BIGINT) as "Contributors"
    FROM '../changeset_data/year=*/month=*/*.parquet' d
    WHERE d.organised_team IS NOT NULL
    GROUP BY d.year, d.organised_team
)
SELECT 
    ym.year,
    ym."Organised Team",
    ym."Edits", 
    ym."Contributors",
    ott.total_edits_all_time as "Total Edits",
    ott.total_contributors_all_time as "Total Contributors"
FROM yearly_metrics ym
JOIN organised_team_totals ott ON ym."Organised Team" = ott."Organised Team"
ORDER BY year DESC, "Edits" DESC
"""
df_all = duckdb.sql(query).df()

table_configs = [
    util.TableConfig(
        title="Edits Per Organised Team by Year",
        label="Edits",
        query_or_df=df_all,
        x_axis_col="year",
        y_axis_col="Organised Team",
        value_col="Edits",
        center_columns=["Rank", "Organised Team"],
        sum_col="Total Edits",
    ),
    util.TableConfig(
        title="Contributors Per Organised Team by Year",
        label="Contributors",
        query_or_df=df_all,
        x_axis_col="year",
        y_axis_col="Organised Team",
        value_col="Contributors",
        center_columns=["Rank", "Organised Team"],
        sum_col="Total Contributors",
    ),
]

util.show_tables(table_configs)
Rank Organised Team 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 Total Edits
1 Meta 0 0 0 0 0 0 0 0 0 78,815 4,296,035 20,247,234 28,957,510 50,673,799 83,060,907 51,973,945 25,494,310 1,074,076 1,431,748 267,288,379
2 Kaart 0 22,922 7,559 364,177 976,391 730,863 780,037 623,276 1,509,967 6,907,406 7,390,957 8,620,818 21,691,873 28,594,458 19,554,990 17,457,242 14,491,270 10,149,722 8,054,283 147,928,211
3 Apple 0 0 0 0 0 0 0 0 0 0 601,578 3,974,945 13,770,839 17,215,025 8,878,281 13,050,921 10,308,898 5,385,095 1,893,899 75,079,481
4 Amazon 0 0 0 0 0 0 0 0 0 0 68 1,171,512 15,006,820 29,407,768 19,940,677 1,456,200 1,034,876 76,744 99,663 68,194,328
5 Mapbox 0 0 45,339 130,377 336,422 1,812,554 2,975,135 5,001,072 10,364,605 7,176,557 6,020,903 5,478,908 1,058,384 1,549,755 1,151,670 1,848,191 1,427,806 1,787,103 3,532,847 51,697,628
6 Microsoft 0 0 846 7,795 0 184 0 0 0 987 44,465 3,308,373 9,463,749 6,588,507 7,790,014 2,157,361 2,245,106 933,667 2,841,323 35,382,377
7 DigitalEgypt 0 0 0 0 0 0 0 0 0 0 0 7,815 4,835,241 4,385,173 3,475,331 4,657,479 4,054,066 3,732,908 1,988,295 27,136,308
8 TomTom 0 0 0 0 0 0 0 0 0 0 13,356 904 14 256 5,163,115 5,383,053 5,994,057 5,209,686 4,006,421 25,770,862
9 Grab 0 0 0 0 0 0 61 10 360,575 158,382 1,122,025 847,285 3,077,576 2,958,667 8,916,251 3,993,110 538,887 377,096 607,807 22,957,732
10 GeoCompas 0 0 0 0 23,957 1,421,771 2,478,856 2,860,814 3,100,322 1,768,042 582,325 1,756,308 580,203 456,227 99,606 844,325 967,220 730,673 950,311 18,620,960
11 Lyft 0 0 0 0 0 0 0 0 0 0 0 18,674 924,763 351,377 333,234 1,668,703 2,370,224 2,255,647 1,238,747 9,161,369
12 VK_Maps 0 0 0 0 0 0 0 0 0 0 19 3,157 19,605 108,003 1,183,951 1,390,851 1,925,937 1,657,395 811,676 7,100,594
13 Ola 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 381,080 3,535,451 1,838,614 625,017 6,380,162
14 Lightcyphers 0 0 0 0 0 0 0 0 0 0 0 792,152 2,358,928 1,790,772 332,143 38 169 3,765 8 5,277,975
15 Uber 0 0 0 0 0 0 0 0 0 0 15 3,313,575 651,199 84,620 18,454 4,134 1,415 3,258 2,303 4,078,973
16 Wildberries Geo Team 0 0 0 0 0 0 0 0 0 0 0 107 9,810 95,944 654,015 599,785 840,244 721,364 871,917 3,793,186
17 Telenav 0 0 0 0 0 0 0 0 58,334 303,600 892,891 1,053,569 384,426 105,963 50,794 70,937 1,696 2 0 2,922,212
18 FixMyCity 12 0 0 0 0 0 2,988 93,720 1,419 69,178 32,469 99,973 19,132 187,495 488,560 596,890 312,207 323,420 412,018 2,639,481
19 Neshan 0 0 0 0 0 0 0 11 34 13 112,540 9 506,381 481,299 198,237 479,892 479,234 298,715 1,003 2,557,368
20 Bolt 0 0 0 0 0 0 0 0 0 0 0 0 0 76,244 364,645 608,904 177,867 13,667 7,988 1,249,315
21 Ozon 0 0 0 0 0 0 0 0 0 0 0 154 0 0 0 0 0 34,748 1,211,485 1,246,387
22 Wonder 0 0 0 0 0 0 0 0 0 0 25,983 88,313 79,845 215,450 144,224 69,255 85,399 0 0 708,469
23 Snapp 0 0 0 0 0 0 0 0 0 1,447 0 44,429 166,913 52,062 83,623 98,455 104,044 64,201 25,311 640,485
24 OpenSidewalks 0 0 0 0 0 0 0 0 0 0 0 0 18,654 80,592 10,728 88,443 196,028 130,770 105,387 630,602
25 Rocketdata.io 0 0 0 0 0 0 0 0 0 0 0 0 0 11,827 71,556 140,653 178,608 154,610 53,745 610,999
26 Snap 0 0 0 0 211 914 645 526 10,196 65,937 23,805 8,679 34,762 34,214 325,971 10,619 422 344 2,981 520,226
27 Graphmasters 0 0 0 0 31 0 0 0 74 35 48 1,081 50,687 283,072 22,588 35,234 37,476 27,312 30,379 488,017
28 Stackbox 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 293,886 159,794 4,376 20,409 478,469
29 TIDBO 0 0 0 0 0 0 0 0 0 0 0 153,262 305,050 8,585 0 3,085 6,445 40 0 476,467
30 TfNSW 0 0 7 0 7 673 825 1,416 2,312 8,747 9,607 30,403 76,672 26,235 88,297 81,260 31,329 35,051 55,403 448,244
31 NextBillion.AI 0 0 0 0 0 0 0 0 0 0 0 0 0 401,141 30 10 0 0 0 401,181
32 AMAT 0 0 0 0 0 0 0 0 0 391 106 9 14,464 1,522 29,674 179,036 60,883 8,193 2,793 297,071
33 Kontur 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2,632 282,941 766 0 0 286,339
34 Geovelo 0 0 0 0 0 0 0 0 0 0 19,937 34,283 36,104 18,496 21,500 20,491 43,063 35,802 54,401 284,077
35 Swiggy 0 0 0 0 0 0 0 0 0 0 0 0 695 7,196 207 1,021 111,108 79,923 26,781 226,931
36 NNG 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 945 150,374 33,036 184,355
37 Expedia 0 0 0 0 0 0 0 0 0 0 0 0 0 2 25,618 57,852 69,542 4,141 379 157,534
38 Digiroad 0 0 0 0 0 0 0 0 0 0 0 905 56,027 1 0 0 2,067 44 0 59,044
39 AppLogica 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 30,001 0 0 0 30,001
40 GOAT 0 0 0 0 0 0 0 0 0 0 165 356 23,845 1,375 820 958 198 354 0 28,071
41 Getpin 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5,219 3,905 906 10,030
42 WIGeoGIS 0 0 0 0 0 0 0 0 517 127 150 285 159 459 221 663 327 301 55 3,264
43 Balad 0 0 0 0 0 0 0 0 0 0 0 8 2,649 0 29 18 18 0 0 2,722
44 Komoot 0 0 0 0 0 0 0 0 0 0 0 0 0 142 400 173 122 443 616 1,896
45 Gojek 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1,185 32 0 56 55 1,328
46 TeleClinic 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 92 87 74 255
47 Blitzer.de 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 27 99 104 230
48 DINAclub 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 71 71