============================================================

CLAUDEPLUGINHUB SKILLS

DESCRIPTIVE ANALYSIS

============================================================

Purpose: Descriptive analysis of the complete ClaudePluginHub Skills dataset scraped from: https://www.claudepluginhub.com/

Main outputs: 1. Dataset profile 2. Data quality 3. Category analysis 4. Technology analysis 5. Popularity analysis 6. Text analysis 7. Author analysis 8. Correlation analysis 9. Popularity concentration 10. Gini coefficient 11. Slide-ready findings

============================================================

============================================================

0. SETUP

============================================================

install.packages(c(
"tidyverse",
"jsonlite",
"scales",
"janitor",
"lubridate",
"tidytext",
"wordcloud",
"RColorBrewer",
"knitr",
"kableExtra"
),
repos = "https://cloud.r-project.org")
## Installing packages into 'C:/Users/khanh/AppData/Local/R/win-library/4.6'
## (as 'lib' is unspecified)
## package 'tidyverse' successfully unpacked and MD5 sums checked
## package 'jsonlite' successfully unpacked and MD5 sums checked
## Warning: cannot remove prior installation of package 'jsonlite'
## Warning in file.copy(savedcopy, lib, recursive = TRUE): problem copying
## C:\Users\khanh\AppData\Local\R\win-library\4.6\00LOCK\jsonlite\libs\x64\jsonlite.dll
## to
## C:\Users\khanh\AppData\Local\R\win-library\4.6\jsonlite\libs\x64\jsonlite.dll:
## Permission denied
## Warning: restored 'jsonlite'
## package 'scales' successfully unpacked and MD5 sums checked
## package 'janitor' successfully unpacked and MD5 sums checked
## package 'lubridate' successfully unpacked and MD5 sums checked
## Warning: cannot remove prior installation of package 'lubridate'
## Warning in file.copy(savedcopy, lib, recursive = TRUE): problem copying
## C:\Users\khanh\AppData\Local\R\win-library\4.6\00LOCK\lubridate\libs\x64\lubridate.dll
## to
## C:\Users\khanh\AppData\Local\R\win-library\4.6\lubridate\libs\x64\lubridate.dll:
## Permission denied
## Warning: restored 'lubridate'
## package 'tidytext' successfully unpacked and MD5 sums checked
## package 'wordcloud' successfully unpacked and MD5 sums checked
## Warning: cannot remove prior installation of package 'wordcloud'
## Warning in file.copy(savedcopy, lib, recursive = TRUE): problem copying
## C:\Users\khanh\AppData\Local\R\win-library\4.6\00LOCK\wordcloud\libs\x64\wordcloud.dll
## to
## C:\Users\khanh\AppData\Local\R\win-library\4.6\wordcloud\libs\x64\wordcloud.dll:
## Permission denied
## Warning: restored 'wordcloud'
## package 'RColorBrewer' successfully unpacked and MD5 sums checked
## package 'knitr' successfully unpacked and MD5 sums checked
## package 'kableExtra' successfully unpacked and MD5 sums checked
## 
## The downloaded binary packages are in
##  C:\Users\khanh\AppData\Local\Temp\Rtmp2XL8WE\downloaded_packages

============================================================

1. FILE PATHS

============================================================

input_file <- "C:/Users/khanh/OneDrive/Desktop/GMU/PHD/ClaudePlugIn/code/claudepluginhub_data/skills.csv"

output_dir <- "output"

table_dir <- file.path(output_dir, "tables")
figure_dir <- file.path(output_dir, "figures")

dir.create(output_dir, showWarnings = FALSE)
dir.create(table_dir, showWarnings = FALSE)
dir.create(figure_dir, showWarnings = FALSE)

============================================================

2. LOAD DATA

============================================================

skills <- read_csv(
input_file,
show_col_types = FALSE
)
skills <- skills %>%
clean_names()

cat("\n====================================================\n")
## 
## ====================================================
cat("CLAUDEPLUGINHUB SKILLS DATASET\n")
## CLAUDEPLUGINHUB SKILLS DATASET
cat("====================================================\n")
## ====================================================
cat("Rows:", nrow(skills), "\n")
## Rows: 282436
cat("Columns:", ncol(skills), "\n\n")
## Columns: 17
cat("Column names:\n")
## Column names:
print(names(skills))
##  [1] "id"                      "component_id"           
##  [3] "name"                    "description"            
##  [5] "allowed_tools"           "supporting_assets_count"
##  [7] "plugin_id"               "plugin_slug"            
##  [9] "plugin_name"             "plugin_display_name"    
## [11] "plugin_stars"            "plugin_categories"      
## [13] "usage_count"             "enriched_technologies"  
## [15] "enriched_topics"         "name_key"               
## [17] "variant_count"

============================================================

3. BASIC DATASET PROFILE

============================================================

dataset_profile <- tibble(
Metric = c(
"Number of skills",
"Number of variables",
"Duplicate rows",
"Total missing cells",
"Total cells"
),

Value = c(
nrow(skills),
ncol(skills),
sum(duplicated(skills)),
sum(is.na(skills)),
nrow(skills) * ncol(skills)
)
)

print(dataset_profile)
## # A tibble: 5 × 2
##   Metric                Value
##   <chr>                 <int>
## 1 Number of skills     282436
## 2 Number of variables      17
## 3 Duplicate rows            0
## 4 Total missing cells  809223
## 5 Total cells         4801412
write_csv(
dataset_profile,
file.path(table_dir, "dataset_profile.csv")
)

============================================================

4. DATA TYPES

============================================================

data_types <- tibble(
Variable = names(skills),
Data_Type = sapply(
skills,
function(x) class(x)[1]
),
Non_Missing = sapply(
skills,
function(x) sum(!is.na(x))
),
Missing = sapply(
skills,
function(x) sum(is.na(x))
),
Missing_Percentage = sapply(
skills,
function(x) mean(is.na(x)) * 100
),
Unique_Values = sapply(
skills,
function(x) n_distinct(x, na.rm = TRUE)
)
)

data_types <- data_types %>%
arrange(desc(Missing_Percentage))

print(data_types)
## # A tibble: 17 × 6
##    Variable       Data_Type Non_Missing Missing Missing_Percentage Unique_Values
##    <chr>          <chr>           <int>   <int>              <dbl>         <int>
##  1 plugin_displa… character       28706  253730           89.8              5093
##  2 allowed_tools  character       36017  246419           87.2             11273
##  3 enriched_tech… character      127906  154530           54.7              8846
##  4 enriched_topi… character      127906  154530           54.7               767
##  5 description    character      282422      14            0.00496        281045
##  6 id             character      282436       0            0              282436
##  7 component_id   character      282436       0            0              271198
##  8 name           character      282436       0            0              282421
##  9 supporting_as… numeric        282436       0            0                  21
## 10 plugin_id      character      282436       0            0               49930
## 11 plugin_slug    character      282436       0            0               49930
## 12 plugin_name    character      282436       0            0               43551
## 13 plugin_stars   numeric        282436       0            0                1185
## 14 plugin_catego… character      282436       0            0                 206
## 15 usage_count    numeric        282436       0            0                 314
## 16 name_key       character      282436       0            0              282421
## 17 variant_count  numeric        282436       0            0                 193
write_csv(
data_types,
file.path(table_dir, "data_types.csv")
)

============================================================

5. MISSING DATA ANALYSIS

============================================================

missing_data <- data_types %>%
arrange(desc(Missing_Percentage))

print(missing_data)
## # A tibble: 17 × 6
##    Variable       Data_Type Non_Missing Missing Missing_Percentage Unique_Values
##    <chr>          <chr>           <int>   <int>              <dbl>         <int>
##  1 plugin_displa… character       28706  253730           89.8              5093
##  2 allowed_tools  character       36017  246419           87.2             11273
##  3 enriched_tech… character      127906  154530           54.7              8846
##  4 enriched_topi… character      127906  154530           54.7               767
##  5 description    character      282422      14            0.00496        281045
##  6 id             character      282436       0            0              282436
##  7 component_id   character      282436       0            0              271198
##  8 name           character      282436       0            0              282421
##  9 supporting_as… numeric        282436       0            0                  21
## 10 plugin_id      character      282436       0            0               49930
## 11 plugin_slug    character      282436       0            0               49930
## 12 plugin_name    character      282436       0            0               43551
## 13 plugin_stars   numeric        282436       0            0                1185
## 14 plugin_catego… character      282436       0            0                 206
## 15 usage_count    numeric        282436       0            0                 314
## 16 name_key       character      282436       0            0              282421
## 17 variant_count  numeric        282436       0            0                 193
missing_plot <- missing_data %>%
filter(Missing > 0) %>%
ggplot(
aes(
x = reorder(Variable, Missing_Percentage),
y = Missing_Percentage
)
) +
geom_col() +
coord_flip() +
labs(
title = "Missing Data by Variable",
x = "Variable",
y = "Missing Values (%)"
) +
theme_minimal()

print(missing_plot)

ggsave(
file.path(figure_dir, "01_missing_data.png"),
missing_plot,
width = 10,
height = 7,
dpi = 300
)

============================================================

6. DUPLICATE ANALYSIS

============================================================

duplicate_count <- sum(duplicated(skills))

cat("\nDuplicate rows:", duplicate_count, "\n")
## 
## Duplicate rows: 0
duplicate_percentage <-
duplicate_count / nrow(skills) * 100

cat(
"Duplicate percentage:",
round(duplicate_percentage, 2),
"%\n"
)
## Duplicate percentage: 0 %

============================================================

7. NUMERIC DESCRIPTIVE STATISTICS

============================================================

numeric_data <- skills %>%
select(where(is.numeric))

if (ncol(numeric_data) > 0) {

numeric_summary <- numeric_data %>%
summarise(
across(
everything(),
list(
count = ~sum(!is.na(.)),
mean = ~mean(., na.rm = TRUE),
median = ~median(., na.rm = TRUE),
sd = ~sd(., na.rm = TRUE),
variance = ~var(., na.rm = TRUE),
minimum = ~min(., na.rm = TRUE),
maximum = ~max(., na.rm = TRUE),
skewness = ~{
x <- .[!is.na(.)]
if (length(x) > 2)
mean((x - mean(x))^3) /
sd(x)^3
else NA
}
)
)
)

print(numeric_summary)

write_csv(
numeric_summary,
file.path(
table_dir,
"numeric_descriptive_statistics.csv"
)
)
}
## # A tibble: 1 × 32
##   supporting_assets_count_count supporting_assets_count…¹ supporting_assets_co…²
##                           <int>                     <dbl>                  <dbl>
## 1                        282436                      1.86                      0
## # ℹ abbreviated names: ¹​supporting_assets_count_mean,
## #   ²​supporting_assets_count_median
## # ℹ 29 more variables: supporting_assets_count_sd <dbl>,
## #   supporting_assets_count_variance <dbl>,
## #   supporting_assets_count_minimum <dbl>,
## #   supporting_assets_count_maximum <dbl>,
## #   supporting_assets_count_skewness <dbl>, plugin_stars_count <int>, …

============================================================

8. IDENTIFY POPULARITY VARIABLES

============================================================

popularity_variables <- names(skills)[
str_detect(
names(skills),
regex(
"star|copy|click|download|install|fork|usage|view",
ignore_case = TRUE
)
)
]

cat("\nPotential popularity variables:\n")
## 
## Potential popularity variables:
print(popularity_variables)
## [1] "plugin_stars" "usage_count"

============================================================

9. GITHUB STARS ANALYSIS

============================================================

if ("plugin_stars" %in% names(skills)) {

stars_summary <- skills %>%
summarise(
Count = sum(!is.na(plugin_stars)),
Mean = mean(plugin_stars, na.rm = TRUE),
Median = median(plugin_stars, na.rm = TRUE),
SD = sd(plugin_stars, na.rm = TRUE),
Minimum = min(plugin_stars, na.rm = TRUE),
Maximum = max(plugin_stars, na.rm = TRUE)
)
}
print(stars_summary)
## # A tibble: 1 × 6
##    Count  Mean Median    SD Minimum Maximum
##    <int> <dbl>  <dbl> <dbl>   <dbl>   <dbl>
## 1 282436  634.      2 7110.       0  291990
write_csv(
stars_summary,
file.path(
table_dir,
"stars_summary.csv"
)
)

Histogram

stars_histogram <- ggplot(
skills,
aes(x = plugin_stars)
) +
geom_histogram(
bins = 50
) +
labs(
title = "Distribution of GitHub Stars",
x = "GitHub Stars",
y = "Number of Skills"
) +
theme_minimal()

print(stars_histogram)

ggsave(
file.path(
figure_dir,
"02_stars_distribution.png"
),
stars_histogram,
width = 10,
height = 6,
dpi = 300
)

Log-transformed distribution

stars_log <- skills %>%
filter(plugin_stars >= 0) %>%
mutate(
log_stars = log10(plugin_stars + 1)
)

stars_log_plot <- ggplot(
stars_log,
aes(x = log_stars)
) +
geom_histogram(
bins = 50
) +
labs(
title = "Log-Transformed Distribution of GitHub Stars",
x = "log10(plugin_stars + 1)",
y = "Number of Skills"
) +
theme_minimal()

print(stars_log_plot)

ggsave(
file.path(
figure_dir,
"03_log_stars_distribution.png"
),
stars_log_plot,
width = 10,
height = 6,
dpi = 300
)

============================================================

10. TOP SKILLS BY STARS

============================================================

if ("plugin_stars" %in% names(skills)) {

top_skills <- skills %>%
arrange(desc(plugin_stars)) %>%
slice_head(n = 20)

print(top_skills)

write_csv(
top_skills,
file.path(
table_dir,
"top_20_skills_by_stars.csv"
)
)
}
## # A tibble: 20 × 17
##    id        component_id name  description allowed_tools supporting_assets_co…¹
##    <chr>     <chr>        <chr> <chr>       <chr>                          <dbl>
##  1 d4b63049… diagnosing-… diag… Investigat… <NA>                              19
##  2 2e6e5eb4… implement    impl… Implements… <NA>                               1
##  3 5e54f489… writing-for… writ… Guides the… <NA>                               2
##  4 d291ab4a… wait-what    wait… Triggers c… <NA>                               1
##  5 e26dedaa… implement-s… impl… Orchestrat… <NA>                               1
##  6 29a167cd… pr           pr    Generates … <NA>                               2
##  7 83323e2e… springboot-… spri… Guides Spr… <NA>                               0
##  8 de6220c6… regex-vs-ll… rege… Decision f… <NA>                               0
##  9 783aa2df… cpp-coding-… cpp-… Enforces C… <NA>                               0
## 10 a394cc78… springboot-… spri… Runs a Spr… <NA>                               0
## 11 c8f654b3… investor-ma… inve… Creates an… <NA>                               0
## 12 ae670291… cpp-testing  cpp-… Guides C++… <NA>                               0
## 13 a03b061c… quality-non… qual… Guides qua… <NA>                               0
## 14 8c4f3160… carrier-rel… carr… Codifies c… <NA>                               0
## 15 110f4eb8… customs-tra… cust… Guides cus… <NA>                               0
## 16 30e2fe2e… energy-proc… ener… Provides c… <NA>                               0
## 17 9f7e3873… inventory-d… inve… Codifies d… <NA>                               0
## 18 f3d60e65… logistics-e… logi… Handles fr… <NA>                               0
## 19 7b9d88b9… production-… prod… Codifies p… <NA>                               0
## 20 be4b8bc8… returns-rev… retu… Codified e… <NA>                               0
## # ℹ abbreviated name: ¹​supporting_assets_count
## # ℹ 11 more variables: plugin_id <chr>, plugin_slug <chr>, plugin_name <chr>,
## #   plugin_display_name <chr>, plugin_stars <dbl>, plugin_categories <chr>,
## #   usage_count <dbl>, enriched_technologies <chr>, enriched_topics <chr>,
## #   name_key <chr>, variant_count <dbl>

============================================================

11. TECHNOLOGY ANALYSIS

============================================================

IMPORTANT:

The scraped technology field may look like:

[]

[“python”]

[“node”]

[“python”, “docker”, “aws”]

Therefore we parse the list rather than treating the

entire string as one technology.

============================================================

names(skills)
##  [1] "id"                      "component_id"           
##  [3] "name"                    "description"            
##  [5] "allowed_tools"           "supporting_assets_count"
##  [7] "plugin_id"               "plugin_slug"            
##  [9] "plugin_name"             "plugin_display_name"    
## [11] "plugin_stars"            "plugin_categories"      
## [13] "usage_count"             "enriched_technologies"  
## [15] "enriched_topics"         "name_key"               
## [17] "variant_count"
if ("enriched_technologies" %in% names(skills)) {

technology_counter <- list()

for (value in skills$enriched_technologies) {


if (is.na(value)) {
  next
}

value <- str_trim(as.character(value))

### Skip empty lists
if (
  value == "" ||
  value == "[]"
) {
  next
}

tryCatch({

  technologies <- fromJSON(value)

  if (is.character(technologies)) {

    for (technology in technologies) {

      technology <- str_trim(
        as.character(technology)
      )

      if (
        technology != "" &&
        !is.na(technology)
      ) {

        technology_counter[[length(
          technology_counter
        ) + 1]] <- technology
      }
    }
  }

}, error = function(e) {

  ### Ignore malformed list values

})


}

technology_counts <- tibble(
Technology = unlist(
technology_counter
)
) %>%
count(
Technology,
sort = TRUE,
name = "Count"
)

print(
technology_counts %>%
slice_head(n = 30)
)

write_csv(
technology_counts,
file.path(
table_dir,
"technology_counts.csv"
)
)
}
## # A tibble: 30 × 2
##    Technology Count
##    <chr>      <int>
##  1 python      7914
##  2 typescript  3635
##  3 git         2673
##  4 github      2337
##  5 javascript  2114
##  6 node        1938
##  7 react       1878
##  8 rest-api    1790
##  9 docker      1748
## 10 css         1615
## # ℹ 20 more rows

Top 30 technology chart

technology_plot <- technology_counts %>%
slice_head(n = 30) %>%
ggplot(
aes(
x = Count,
y = reorder(
Technology,
Count
)
)
) +
geom_col() +
labs(
title = "Top 30 Technologies Used by Skills",
x = "Number of Skills",
y = "Technology"
) +
theme_minimal()

print(technology_plot)

ggsave(
file.path(
figure_dir,
"04_top_technologies.png"
),
technology_plot,
width = 10,
height = 7,
dpi = 300
)

============================================================

12. CATEGORY ANALYSIS

============================================================

if (“category” %in% names(skills)) {

category_counts <- skills %>% filter( !is.na(category), str_trim(as.character(category)) != “” ) %>% count( category, sort = TRUE )

print(category_counts)

write_csv( category_counts, file.path( table_dir, “category_counts.csv” ) )

category_plot <- category_counts %>% slice_head(n = 20) %>% ggplot( aes( x = n, y = reorder(category, n) ) ) + geom_col() + labs( title = “Most Common Skill Categories”, x = “Number of Skills”, y = “Category” ) + theme_minimal()

print(category_plot)

ggsave( file.path( figure_dir, “05_skill_categories.png” ), category_plot, width = 10, height = 7, dpi = 300 ) }

============================================================

13. AUTHOR ANALYSIS

============================================================

author_column <- names(skills)[ str_detect( names(skills), regex( “author|owner|publisher”, ignore_case = TRUE ) )][1]

if (!is.na(author_column)) {

author_counts <- skills %>% filter( !is.na(.data[[author_column]]), str_trim( as.character(.data[[author_column]]) ) != “” ) %>% count( .data[[author_column]], sort = TRUE, name = “Skills” )

print( author_counts %>% slice_head(n = 20) )

write_csv( author_counts, file.path( table_dir, “author_counts.csv” ) ) }

============================================================

14. DESCRIPTION LENGTH

============================================================

description_column <- names(skills)[
str_detect(
names(skills),
regex(
"description",
ignore_case = TRUE
)
)
][1]

if (!is.na(description_column)) {

skills <- skills %>%
mutate(
description_length =
str_length(
as.character(
.data[[description_column]]
)
)
)

description_summary <- skills %>%
summarise(
Count = sum(
!is.na(description_length)
),
Mean = mean(
description_length,
na.rm = TRUE
),
Median = median(
description_length,
na.rm = TRUE
),
SD = sd(
description_length,
na.rm = TRUE
),
Minimum = min(
description_length,
na.rm = TRUE
),
Maximum = max(
description_length,
na.rm = TRUE
)
)
print("Description summary")
print(description_summary)

write_csv(
description_summary,
file.path(
table_dir,
"description_length_summary.csv"
)
)
}
## [1] "Description summary"
## # A tibble: 1 × 6
##    Count  Mean Median    SD Minimum Maximum
##    <int> <dbl>  <dbl> <dbl>   <int>   <int>
## 1 282422  267.    197  206.       1   16724
description_plot <- ggplot(
skills,
aes(x = description_length)
) +
geom_histogram(
bins = 50
) +
labs(
title = "Distribution of Skill Description Length",
x = "Description Length (characters)",
y = "Number of Skills"
) +
theme_minimal()

print(description_plot)

ggsave(
file.path(
figure_dir,
"06_description_length.png"
),
description_plot,
width = 10,
height = 6,
dpi = 300
)

============================================================

15. TEXT ANALYSIS

============================================================

if (!is.na(description_column)) {

description_words <- skills %>%
select(
description = all_of(
description_column
)
) %>%
filter(
!is.na(description)
) %>%
unnest_tokens(
word,
description
) %>%
anti_join(
stop_words,
by = "word"
) %>%
filter(
str_detect(
word,
"^[a-zA-Z][a-zA-Z0-9+#.-]*$"
)
) %>%
count(
word,
sort = TRUE
)

print(
description_words %>%
slice_head(n = 30)
)

write_csv(
description_words,
file.path(
table_dir,
"description_word_frequency.csv"
)
)

word_plot <- description_words %>%
slice_head(n = 20) %>%
ggplot(
aes(
x = n,
y = reorder(word, n)
)
) +
geom_col() +
labs(
title = "Most Frequent Terms in Skill Descriptions",
x = "Frequency",
y = "Word"
) +
theme_minimal()

print(word_plot)

ggsave(
file.path(
figure_dir,
"07_description_keywords.png"
),
word_plot,
width = 10,
height = 7,
dpi = 300
)
}
## # A tibble: 30 × 2
##    word        n
##    <chr>   <int>
##  1 user    56649
##  2 code    36780
##  3 skill   35372
##  4 review  30336
##  5 data    27255
##  6 design  25259
##  7 agent   24266
##  8 project 22302
##  9 und     20821
## 10 de      20815
## # ℹ 20 more rows

============================================================

16. CORRELATION ANALYSIS

============================================================

numeric_data <- skills %>%
select(where(is.numeric))

if (ncol(numeric_data) >= 2) {

correlation_matrix <- cor(
numeric_data,
use = "pairwise.complete.obs"
)

print(
round(
correlation_matrix,
2
)
)

correlation_long <- as.data.frame(
correlation_matrix
) %>%
rownames_to_column(
"Variable_1"
) %>%
pivot_longer(
-Variable_1,
names_to = "Variable_2",
values_to = "Correlation"
)

write_csv(
correlation_long,
file.path(
table_dir,
"correlation_matrix.csv"
)
)
}
##                         supporting_assets_count plugin_stars usage_count
## supporting_assets_count                    1.00         0.00       -0.03
## plugin_stars                               0.00         1.00        0.50
## usage_count                               -0.03         0.50        1.00
## variant_count                              0.00         0.11        0.23
## description_length                         0.16        -0.04       -0.06
##                         variant_count description_length
## supporting_assets_count          0.00               0.16
## plugin_stars                     0.11              -0.04
## usage_count                      0.23              -0.06
## variant_count                    1.00              -0.04
## description_length              -0.04               1.00

============================================================

17. TOP-N POPULARITY CONCENTRATION

============================================================

Research question:

Is the Claude Code skill ecosystem highly concentrated

============================================================

if ("plugin_stars" %in% names(skills)) {

plugin_stars <- skills %>%
filter(
!is.na(plugin_stars),
plugin_stars >= 0
) %>%
arrange(desc(plugin_stars)) %>%
pull(plugin_stars)

total_stars <- sum(plugin_stars)

top_n_values <- c(
10,
20,
50,
100,
500
)

concentration <- tibble(
Top_N = top_n_values
) %>%
mutate(
Stars = map_dbl(
Top_N,
~sum(
head(
plugin_stars,
min(.x, length(plugin_stars))
)
)
),
Percentage_of_Total =
Stars / total_stars * 100
)

print(concentration)

write_csv(
concentration,
file.path(
table_dir,
"popularity_concentration.csv"
)
)
}
## # A tibble: 5 × 3
##   Top_N    Stars Percentage_of_Total
##   <dbl>    <dbl>               <dbl>
## 1    10  2708722                1.51
## 2    20  5383902                3.01
## 3    50 13409442                7.49
## 4   100 26785342               15.0 
## 5   500 56327139               31.5

Concentration plot

concentration_plot <- ggplot(
concentration,
aes(
x = Top_N,
y = Percentage_of_Total
)
) +
geom_line() +
geom_point(size = 3) +
scale_x_continuous(
trans = "log10",
breaks = top_n_values
) +
scale_y_continuous(
labels = function(x) {
paste0(
x,
"%"
)
}
) +
labs(
title = "Concentration of GitHub Stars",
subtitle =
"Share of total stars captured by the top N skills",
x = "Top N Skills (log scale)",
y = "Percentage of Total Stars"
) +
theme_minimal()

print(concentration_plot)

ggsave(
file.path(
figure_dir,
"08_star_concentration.png"
),
concentration_plot,
width = 10,
height = 6,
dpi = 300
)

============================================================

18. PARETO THRESHOLDS

============================================================

Determine how many skills account for:

50%

75%

80%

90%

95%

of total GitHub stars.

============================================================

if ("plugin_stars" %in% names(skills)) {

pareto <- skills %>%
filter(
!is.na(plugin_stars),
plugin_stars >= 0
) %>%
arrange(desc(plugin_stars)) %>%
mutate(
cumulative_stars =
cumsum(plugin_stars),


  cumulative_percentage =
    cumulative_stars /
    sum(plugin_stars) *
    100,

  percentage_of_skills =
    row_number() /
    n() *
    100
)


thresholds <- c(
50,
75,
80,
90,
95
)

pareto_results <- tibble(
Target_Percentage = thresholds
) %>%
rowwise() %>%
mutate(
Number_of_Skills = which(
pareto$cumulative_percentage >=
Target_Percentage
)[1]
) %>%
ungroup() %>%
mutate(
Percentage_of_Skills =
Number_of_Skills /
nrow(pareto) *
100
)

print(pareto_results)

write_csv(
pareto_results,
file.path(
table_dir,
"pareto_analysis.csv"
)
)
}
## # A tibble: 5 × 3
##   Target_Percentage Number_of_Skills Percentage_of_Skills
##               <dbl>            <int>                <dbl>
## 1                50             1207                0.427
## 2                75             2281                0.808
## 3                80             2599                0.920
## 4                90             5501                1.95 
## 5                95            12670                4.49

============================================================

19. GINI COEFFICIENT

============================================================

calculate_gini <- function(x) {

x <- x[
!is.na(x) &
x >= 0
]

if (length(x) == 0) {
return(NA_real_)
}

if (sum(x) == 0) {
return(0)
}

x <- sort(x)

n <- length(x)

index <- seq_len(n)

gini <- (
2 * sum(
index * x
) /
(n * sum(x))
-
(n + 1) / n
)

return(gini)
}

if ("plugin_stars" %in% names(skills)) {

gini_stars <- calculate_gini(
skills$plugin_stars
)

cat(
"\nGini coefficient for GitHub stars:",
round(
gini_stars,
4
),
"\n"
)

gini_result <- tibble(
Measure =
"GitHub Stars",
Gini_Coefficient =
gini_stars
)

write_csv(
gini_result,
file.path(
table_dir,
"gini_coefficient.csv"
)
)
}
## 
## Gini coefficient for GitHub stars: 0.9797

============================================================

20. SLIDE-READY KEY FINDINGS

============================================================

names(skills)
##  [1] "id"                      "component_id"           
##  [3] "name"                    "description"            
##  [5] "allowed_tools"           "supporting_assets_count"
##  [7] "plugin_id"               "plugin_slug"            
##  [9] "plugin_name"             "plugin_display_name"    
## [11] "plugin_stars"            "plugin_categories"      
## [13] "usage_count"             "enriched_technologies"  
## [15] "enriched_topics"         "name_key"               
## [17] "variant_count"           "description_length"
cat("\n")
cat("====================================================\n")
## ====================================================
cat("KEY FINDINGS FOR PRESENTATION\n")
## KEY FINDINGS FOR PRESENTATION
cat("====================================================\n")
## ====================================================
cat(
"\n1. DATASET SIZE\n"
)
## 
## 1. DATASET SIZE
cat(
"The dataset contains",
format(
nrow(skills),
big.mark = ","
),
"skills across",
ncol(skills),
"variables.\n"
)
## The dataset contains 282,436 skills across 18 variables.
if ("plugin_stars" %in% names(skills)) {

cat(
"\n2. POPULARITY\n"
)

cat(
"Total GitHub stars:",
format(
sum(
skills$plugin_stars,
na.rm = TRUE
),
big.mark = ","
),
"\n"
)

cat(
"Median GitHub stars:",
format(
median(
skills$plugin_stars,
na.rm = TRUE
),
big.mark = ","
),
"\n"
)

cat(
"\n3. POPULARITY CONCENTRATION\n"
)

top_500 <- min(
500,
length(
skills$plugin_stars[
!is.na(skills$plugin_stars)
]
)
)

sorted_stars <- sort(
skills$plugin_stars[
!is.na(skills$plugin_stars)
],
decreasing = TRUE
)

concentration_500 <-
sum(
head(
sorted_stars,
top_500
)
) /
sum(
sorted_stars
) *
100

cat(
"Top",
top_500,
"skills account for",
round(
concentration_500,
2
),
"% of total GitHub stars.\n"
)

cat(
"\n4. INEQUALITY\n"
)

cat(
"Gini coefficient:",
round(
gini_stars,
4
),
"\n"
)
}
## 
## 2. POPULARITY
## Total GitHub stars: 179,001,899 
## Median GitHub stars: 2 
## 
## 3. POPULARITY CONCENTRATION
## Top 500 skills account for 31.47 % of total GitHub stars.
## 
## 4. INEQUALITY
## Gini coefficient: 0.9797
cat("\n====================================================\n")
## 
## ====================================================
cat("ANALYSIS COMPLETE\n")
## ANALYSIS COMPLETE
cat("====================================================\n")
## ====================================================
cat(
"\nTables saved to:",
table_dir,
"\n"
)
## 
## Tables saved to: output/tables
cat(
"Figures saved to:",
figure_dir,
"\n"
)
## Figures saved to: output/figures