Aquest conjunt de dades prove de la pàgina web Kaggle. L’objectiu del projecte es analitzar i entendre la relació entre la mida inicial d’un joc i la seva capacitat de créixer de manera sostinguda.
Es a dir, l’objectiu es respondre la pregunta que ens vam plantejar.
La pregunta que ens vam plantejar va ser la següent: Els jocs que presenten un creixement percentual (gain_percent) positiu i sostingut són generalment jocs que ja tenien una base de jugadors mitjana (avg_players) petita o gran a l’inici del període de creixement?
# dim(datos)
# glimpse(datos)
| variable | tipus | Descripció | Valors possibles / Rang |
|---|---|---|---|
| month | character | Mes de mesura i any | Format Mes-Any |
| avg_players | numeric | Base mitjana de jugadors | 0 fins a milions |
| gain | character | Creixement de jugadors | -∞ fins ∞ |
| gain_percent | numeric | Creixement percentual | -∞ fins ∞ |
| peak_players | integer | Nombre màxim de jugadors | 1 fins a milions |
| name | character | Nom del títol del joc | 0–100 caràcters |
| steam_appid | integer | ID únic del joc | 0 fins 1.000.000 |
Les variables que mes hem fet servir són les següents: avg_players->Mitjana de jugadors simultanis gain_percent->Percentatge de creixement o perdua dels jugadors respecte el mes anterior month->Temporalitat de les dades
Hem netejat la variable gain_percent i hem creat variables per categoritzar el tamany: Molt petit,petit,mitja,gran.
Hem fet tambe variables per dividir per estacions del any, també hem segmentat les dades en èpoques: Pre-Covid,Covid i Post-Covid.
# tibble(
# variable = names(datos),
# tipus = sapply(datos, class)
# )
Per analitzar les dades s’han aplicat les següents tècniques:
Anàlisi Exploratòria (EDA): Ús d’histogrames amb escala logarítmica i corbes de densitat per entendre la distribució de jugadors.
Test de Chi-quadrat d’Independència: Per determinar si existeix una relació entre la mida del joc (popularitat) i la seva tendència de creixement, així com la relació entre les estacions de l’any i el creixement.
Test de Kruskal-Wallis: Com a alternativa no paramètrica a l’ANOVA, utilitzat per comparar si el creixement (gain_percent) difereix significativament entre les tres èpoques definides (COVID-19).
df <- load("steamcharts.RData")
library(ggplot2)
library(dplyr)
str(datos)
## 'data.frame': 612265 obs. of 7 variables:
## $ month : chr "Sep-25" "Aug-25" "Jul-25" "Jun-25" ...
## $ avg_players : num 7805 6922 7371 8205 9053 ...
## $ gain : chr "883.12" "-449.35" "-833.5" "-847.53" ...
## $ gain_percent: num 0.1276 -0.061 -0.1016 -0.0936 -0.0495 ...
## $ peak_players: int 13254 12168 13951 15798 15333 17727 18180 18934 20626 19006 ...
## $ name : chr "Counter-Strike" "Counter-Strike" "Counter-Strike" "Counter-Strike" ...
## $ steam_appid : int 10 10 10 10 10 10 10 10 10 10 ...
head(datos)
## month avg_players gain gain_percent peak_players name
## 1 Sep-25 7805.25 883.12 0.1276 13254 Counter-Strike
## 2 Aug-25 6922.13 -449.35 -0.0610 12168 Counter-Strike
## 3 Jul-25 7371.48 -833.5 -0.1016 13951 Counter-Strike
## 4 Jun-25 8204.98 -847.53 -0.0936 15798 Counter-Strike
## 5 May-25 9052.51 -471.31 -0.0495 15333 Counter-Strike
## 6 Apr-25 9523.82 -849.53 -0.0819 17727 Counter-Strike
## steam_appid
## 1 10
## 2 10
## 3 10
## 4 10
## 5 10
## 6 10
ggplot(datos, aes(x = avg_players, y = gain_percent)) +
geom_point(alpha = 0.6) +
labs(
title = "Relació entre avg_players i gain_percent",
x = "avg_players",
y = "gain_percent"
)
summary(datos[, c("avg_players", "gain_percent")])
## avg_players gain_percent
## Min. :0.000e+00 Min. : -1.00
## 1st Qu.:2.530e+00 1st Qu.: -0.17
## Median :1.061e+01 Median : -0.02
## Mean :5.933e+02 Mean : 14.71
## 3rd Qu.:6.046e+01 3rd Qu.: 0.15
## Max. :1.585e+06 Max. :1622960.34
sd(datos$avg_players, na.rm = TRUE)
## [1] 11226.13
sd(datos$gain_percent, na.rm = TRUE)
## [1] 3085.757
library(ggplot2)
ggplot(datos, aes(avg_players)) +
geom_histogram(bins = 50, fill = "steelblue", alpha = 0.7) +
scale_x_continuous(trans = "log10") +
labs(
title = "Distribució de la base de jugadors (avg_players)",
x = "avg_players (log scale)",
y = "Freqüència"
)
## Warning in scale_x_continuous(trans = "log10"): log-10 transformation
## introduced infinite values.
## Warning: Removed 1235 rows containing non-finite outside the scale range
## (`stat_bin()`).
ggplot(datos, aes(gain_percent)) +
geom_histogram(bins = 50, fill = "darkgreen", alpha = 0.7) +
xlim(-1, 1) +
labs(
title = "Distribució de gain_percent (limitat a [-1, 1])",
x = "gain_percent",
y = "Freqüència"
)
## Warning: Removed 31305 rows containing non-finite outside the scale range
## (`stat_bin()`).
## Warning: Removed 2 rows containing missing values or values outside the scale range
## (`geom_bar()`).
datos$size_group <- cut(
datos$avg_players,
breaks = c(0, 50, 500, 5000, Inf),
labels = c("Molt petit", "Petit", "Mitjà", "Gran")
)
df_lim <- datos[datos$gain_percent >= -1 & datos$gain_percent <= 1, ]
ggplot(df_lim, aes(x = gain_percent, color = size_group)) +
geom_density(linewidth = 1) +
labs(
title = "Distribució de 'gain_percent' per mida (Corbes de Densitat)",
x = "Porcentatge de Guany",
y = "Densitat",
color = "Mida del joc"
)
df_lim <- df_lim %>%
mutate(
Creixement_Binary = ifelse(gain_percent > 0, "Positiu", "Negatiu o Zero")
)
desviacio_estandard_global <- sd(df_lim$avg_players, na.rm = TRUE)
print(paste("Desviació Estàndard de Avg Players (GLOBAL):", desviacio_estandard_global))
## [1] "Desviació Estàndard de Avg Players (GLOBAL): 11505.4099917134"
mitjana_global <- mean(df_lim$avg_players, na.rm = TRUE)
mediana_global <- median(df_lim$avg_players, na.rm = TRUE)
print(paste("Mitjana de Avg Players (GLOBAL):", mitjana_global))
## [1] "Mitjana de Avg Players (GLOBAL): 609.034417842881"
print(paste("Mediana de Avg Players (GLOBAL):", mediana_global))
## [1] "Mediana de Avg Players (GLOBAL): 10.73"
dim(datos)
## [1] 612265 8
glimpse(datos)
## Rows: 612,265
## Columns: 8
## $ month <chr> "Sep-25", "Aug-25", "Jul-25", "Jun-25", "May-25", "Apr-25…
## $ avg_players <dbl> 7805.25, 6922.13, 7371.48, 8204.98, 9052.51, 9523.82, 103…
## $ gain <chr> "883.12", "-449.35", "-833.5", "-847.53", "-471.31", "-84…
## $ gain_percent <dbl> 0.1276, -0.0610, -0.1016, -0.0936, -0.0495, -0.0819, -0.1…
## $ peak_players <int> 13254, 12168, 13951, 15798, 15333, 17727, 18180, 18934, 2…
## $ name <chr> "Counter-Strike", "Counter-Strike", "Counter-Strike", "Co…
## $ steam_appid <int> 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 1…
## $ size_group <fct> Gran, Gran, Gran, Gran, Gran, Gran, Gran, Gran, Gran, Gra…
library(tidyverse)
tibble(
variable = names(datos),
tipus = sapply(datos, class)
)
## # A tibble: 8 × 2
## variable tipus
## <chr> <chr>
## 1 month character
## 2 avg_players numeric
## 3 gain character
## 4 gain_percent numeric
## 5 peak_players integer
## 6 name character
## 7 steam_appid integer
## 8 size_group factor
summary(datos)
## month avg_players gain gain_percent
## Length:612265 Min. :0.000e+00 Length:612265 Min. : -1.00
## Class :character 1st Qu.:2.530e+00 Class :character 1st Qu.: -0.17
## Mode :character Median :1.061e+01 Mode :character Median : -0.02
## Mean :5.933e+02 Mean : 14.71
## 3rd Qu.:6.046e+01 3rd Qu.: 0.15
## Max. :1.585e+06 Max. :1622960.34
## peak_players name steam_appid size_group
## Min. : 0 Length:612265 Min. : 10 Molt petit:443799
## 1st Qu.: 10 Class :character 1st Qu.:221260 Petit :120200
## Median : 32 Mode :character Median :336610 Mitjà : 37526
## Mean : 1193 Mean :346922 Gran : 9505
## 3rd Qu.: 162 3rd Qu.:502800 NA's : 1235
## Max. :3236027 Max. :802870
library(dplyr)
df <- as.data.frame(datos)
df <- df %>%
mutate(
avg_players = as.numeric(avg_players),
gain_percent = as.numeric(gsub("%", "", gain_percent)),
popularity_category = cut(avg_players,
breaks = c(-Inf, 100, 1000, 10000, Inf),
labels = c("Low", "Medium", "High", "Very High")),
trend_category = cut(gain_percent,
breaks = c(-Inf, 0, 0.05, Inf),
labels = c("Loss", "Stable", "Growth"))
)
head(df)
## month avg_players gain gain_percent peak_players name
## 1 Sep-25 7805.25 883.12 0.1276 13254 Counter-Strike
## 2 Aug-25 6922.13 -449.35 -0.0610 12168 Counter-Strike
## 3 Jul-25 7371.48 -833.5 -0.1016 13951 Counter-Strike
## 4 Jun-25 8204.98 -847.53 -0.0936 15798 Counter-Strike
## 5 May-25 9052.51 -471.31 -0.0495 15333 Counter-Strike
## 6 Apr-25 9523.82 -849.53 -0.0819 17727 Counter-Strike
## steam_appid size_group popularity_category trend_category
## 1 10 Gran High Growth
## 2 10 Gran High Loss
## 3 10 Gran High Loss
## 4 10 Gran High Loss
## 5 10 Gran High Loss
## 6 10 Gran High Loss
taula_contingencia <- table(df$popularity_category, df$trend_category)
print(taula_contingencia)
##
## Loss Stable Growth
## Low 273327 37747 181418
## Medium 48718 9350 32465
## High 12147 2822 8353
## Very High 2965 847 2106
resultat_chi <- chisq.test(taula_contingencia)
print(resultat_chi)
##
## Pearson's Chi-squared test
##
## data: taula_contingencia
## X-squared = 1486.1, df = 6, p-value < 2.2e-16
p_valor <- resultat_chi$p.value
cat("El p-valor obtingut és:", p_valor, "\n")
## El p-valor obtingut és: 5.577774e-318
library(dplyr)
library(lubridate)
df$date <- my(df$month)
df$month_num <- month(df$date)
df <- df %>%
mutate(season = case_when(
month_num %in% c(12, 1, 2) ~ "Winter",
month_num %in% c(3, 4, 5) ~ "Spring",
month_num %in% c(6, 7, 8) ~ "Summer",
month_num %in% c(9, 10, 11) ~ "Autumn"
))
df$season <- factor(df$season, levels = c("Winter", "Spring", "Summer", "Autumn"))
taula_estacions <- table(df$season, df$trend_category)
prop.table(taula_estacions, 1) * 100
##
## Loss Stable Growth
## Winter 50.461819 8.949977 40.588204
## Spring 59.756201 7.714418 32.529381
## Summer 54.905236 8.014913 37.079851
## Autumn 55.072330 8.508317 36.419353
library(ggplot2)
ggplot(df, aes(x = season, fill = trend_category)) +
geom_bar(position = "fill") +
labs(y = "Proporció", x = "Estació de l'any", title = "Tendència de jugadors per Estació") +
scale_y_continuous(labels = scales::percent) +
theme_minimal()
chi_season <- chisq.test(taula_estacions)
print(chi_season)
##
## Pearson's Chi-squared test
##
## data: taula_estacions
## X-squared = 2711.3, df = 6, p-value < 2.2e-16
Sys.setlocale("LC_TIME", "C")
## [1] "C"
library(dplyr)
library(ggplot2)
df <- df %>%
mutate(
data_aux = as.Date(paste0("01-", month), format = "%d-%b-%y"),
era = case_when(
data_aux < as.Date("2020-03-01") ~ "Pre-COVID",
data_aux >= as.Date("2020-03-01") & data_aux <= as.Date("2021-12-01") ~ "COVID Peak",
data_aux > as.Date("2021-12-01") ~ "Post-COVID"
),
era = factor(era, levels = c("Pre-COVID", "COVID Peak", "Post-COVID"))
)
resum_era <- df %>%
group_by(era) %>%
summarise(
n_observacions = n(),
mitjana_avg_players = mean(avg_players, na.rm = TRUE),
mitjana_gain_percent = mean(gain_percent, na.rm = TRUE)
)
print(resum_era)
## # A tibble: 3 × 4
## era n_observacions mitjana_avg_players mitjana_gain_percent
## <fct> <int> <dbl> <dbl>
## 1 Pre-COVID 314813 462. 18.9
## 2 COVID Peak 106709 629. 20.7
## 3 Post-COVID 190743 790. 4.40
ggplot(df, aes(x = era, y = avg_players, fill = era)) +
geom_boxplot() +
scale_y_log10() +
labs(
title = "Distribució de Jugadors Mitjans per Època",
subtitle = "Escala logarítmica per visualitzar jocs de totes les mides",
y = "Log(Avg Players)",
x = "Època"
) +
theme_minimal()
## Warning in scale_y_log10(): log-10 transformation introduced infinite values.
## Warning: Removed 1235 rows containing non-finite outside the scale range
## (`stat_boxplot()`).
ggplot(df, aes(x = era, y = gain_percent, fill = era)) +
geom_boxplot() +
coord_cartesian(ylim = c(-0.5, 0.5)) +
labs(
title = "Taxa de Creixement (Gain Percent) per Època",
y = "Gain Percent",
x = "Època"
) +
theme_minimal()
kruskal.test(gain_percent ~ era, data = df)
##
## Kruskal-Wallis rank sum test
##
## data: gain_percent by era
## Kruskal-Wallis chi-squared = 816.35, df = 2, p-value < 2.2e-16
En general podem veure que les dades de jugadors presenten una forta asimetria positiva, per aixo fem servir logaritmes i tests no paramètrics.
Interpretació i Conclusions
Impacte del COVID-19: El test de Kruskal-Wallis (p-valor < 2.2e-16) confirma que la pandèmia va ser un factor el qual va augmentar significativament el creixement de jugadors.
Estacionalitat: Es rebutja la hipòtesi nul·la d’independència; l’hivern i l’estiu mostren un creixement superior, probablement per les vacances i les rebaixes de Steam.
Mida: La popularitat està lligada a l’estabilitat. Un joc gran és menys probable que experimenti creixements o caigudes brusques en comparació amb un de petit.
Limitacions
Variables Externes: Podiem haver mirat mes factors com ara preu dels jocs genere entre altres els cuals podrien donar valors mes realistes.
Possibles Línies Futures
Estudiar si els jocs “Indie” tenen comportament diferent als “AAA”, mirar el creixement per generes, preus entre altres.