Disclaimer: Todos os cenários, descrições, e situações deste projeto são fictícios e não representam qualquer relação com alguma empresa real. Portanto, os processos, resultados, recomendações, ou qualquer outra informação presente neste projeto possuem a única intenção de descrever minhas habilidades em ciência de dados. Os dados utilizados neste projeto foram coletados de um domínio público do website Kaggle. Esta base de dados está em domínio público e sua utilização respeita todos os termo e condições do website.
A empresa
GoalForce Analytics é uma empresa que avalia jovens jogadores de futebol, visando detectar indicadores que demonstrem potencial de sucesso dos mesmos.
O CEO da GoalForce Analytics requisitou a equipe de cientistas de dados uma solução do tipo insights que orientasse qual posição de atuação no clube o jogador deveria investir, visando retorno financeiro para um jovem jogador de futebol.
Para entregar o produto final, solicitado pelo CEO da empresa, existem algum passos que o cientista de dados deve percorrer, que incluem:
Ferramentas utilizadas
A justificativa para utilização da linguagem R se deu pelo planejamento para os próximos ciclos deste projeto. Para o primeiro ciclo, a abordagem do projeto será do tipo insights. Para o ciclo 2 em diante, será feita uma abordagem de rergessão, utilizando modelagem linear mista. A linguagem R já possui ferramentas desenvolvidas para realizar tais técnicas. Além disso, devido ao planejamento dos ciclos, optei por já iniciar o projeto no R, uma vez que o mesmo também possui boas ferramentas de manipulação e visualização de dados.
Após estudo detalhado do pedido do CEO, algumas premissas devem ser levadas em consideração. Abaixo encontra - se as premissas consideradas para este projeto:
Os dados para esse projeto foram coletados no site Kaggle. As informações de jogadores de futebol profissionais de oito temporadas estão cadastrados no jogo FIFA do estúdio EA Sports. Os dados utilizados se referem a um histórico dos jogadores no período, e apresentam características da aptidão física e abordagem técnico-tática dos mesmos. Embora se tratem de dados sintéticos, os atributos podem ser coletados em jogares no mundo real e os métodos de ciência de dados aqui implementados, podem ser aplicados no mundo real. Os dados podem ser baixados aqui.
A página do Kaggle disponibiliza os dados em links individuais, e os arquivos estão em formato .csv, formatados como tabelas e disponíveis para download. O autor dos conjuntos de dados afirma que os dados são oriundos de procedimento de webscraping, o que sugere que possíveis vieses seriam oriundos dos códigos utilizados na coleta de dados.
Os dados utilizados respeitam os termos de utilização do Kaggle. Os dados foram armazenados em maquina local e protegidos por senha. Após download, eu realizei rápida inspeção visual, procurando por problemas visíveis, para preparar os dados para processamento e análise. Este cuidado inicial dom os dados visa ampliar a qualidade das análises e interpretação dos resultados.
Para carregar os dados no ambiente de trabalho bem como desenvolver todos os procedimentos do projeto, será necessário carregar algumas bibliotecas que não são nativas no R.
library( readxl )
library( dplyr )
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library( ggplot2 )
library( reshape2 )
library( psych )
##
## Attaching package: 'psych'
## The following objects are masked from 'package:ggplot2':
##
## %+%, alpha
library( knitr )
library( pander )
library( kableExtra )
##
## Attaching package: 'kableExtra'
## The following object is masked from 'package:dplyr':
##
## group_rows
library( stringr )
Os dados deste projeto foram disponibilizados em arquivos separados (possivelmente, devido ao tamanho de cada arquivo. Também, arquivos separados facilitam a análise de diferentes temporadas sem a necessidade de linhas de código adicionais).
Para carregar os dados, primeiro eu listei todos os arquivos .csv no ambiente de trabalho:
csv_files <- list.files( pattern = "*.csv" )
Então, carreguei e nomeei cada arquivo de acordo com a temporada que o mesmo representa:
data_list <- list()
for ( file in csv_files ){
#extract the file name without the extension
file_name <- gsub( "\\.csv", "", file, ignore.case=TRUE )
# Load csv into data frame
df <- read.csv( file, header=TRUE )
# assign the data frame to an object with the same name
assign( file_name, df )
#add the data frame to the list
data_list[[file_name]] <- df
}
Em seguida, criei uma lista com todos os data frames do ambiente, realizei pequena correção nos tipos de dados nas variáveis “lw” e “rw” para poder concatenar todos os data frames em um único arquivo. o último bloco de códigos na célula abaixo removou aqueles arquivos que não serão mais utilizados, visando liberar recursos do computador local:
# get a list of all data frames in the environment
df_list <- Filter( is.data.frame,mget( ls() ) )
# fix data types for concatenation
fifa_17$lw <- as.character( fifa_17$lw )
fifa_18$lw <- as.character( fifa_18$lw )
fifa_21$lw <- as.character( fifa_21$lw )
fifa_22$lw <- as.character( fifa_22$lw )
fifa_17$rw <- as.character( fifa_17$rw )
fifa_21$rw <- as.character( fifa_21$rw )
fifa_22$rw <- as.character( fifa_22$rw )
# concatenate all data frames into single data frame
fifa_15to20 <- bind_rows( fifa_15,fifa_16,fifa_17,fifa_18,fifa_19,fifa_20 )
fifa_rest <- bind_rows( fifa_15to20,fifa_21 )
fifas <- bind_rows( fifa_rest,fifa_22 )
# remove unused data
rm( fifa_15,fifa_16,fifa_17,fifa_18,fifa_19,fifa_20,fifa_21,fifa_22,fifa_15to20,fifa_rest, fifa_rkd, data_list, df, df_list, file, file_name, csv_files )
Neste passo, serão feitas descrição dos atributos do conjunto de dados, verificação da dimensão do data frame com as oito temporadas, verificados os tipos de variáveis, se existem dados faltantes, e realizar uma análise estatística descritiva.
Tabela 1. Descrição dos atributos do data frame original.
| var_name | description |
|---|---|
| sofifa_id | unique player ID on sofifa |
| player_url | URL of the scraped player |
| short_name | player short name |
| long_name | player long name |
| player_positions | player preferred positions |
| overall | player current overall attribute |
| potential | player potential overall attribute |
| value_eur | player value (in EUR) |
| wage_eur | player weekly wage (in EUR) |
| age | player age |
| dob | player date of birth |
| height_cm | player height (in cm) |
| weight_kg | player weight (in kg) |
| club_team_id | club team_id on sofifa where the player plays |
| club_name | club name where the player plays |
| league_name | league name of the club |
| league_level | league rank of the club (e.g. English Premier League is 1, English League Championship is 2, etc.) |
| club_position | player position in the club (e.g. SUB means substitute, RES means reserve) |
| club_jersey_number | player jersey number in the club |
| club_loaned_from | club loaning out the player - if applicable |
| club_joined | date when the player joined his current club |
| club_contract_valid_until | player contract expiration date |
| nationality_id | player nationality id on sofifa |
| nationality_name | player nationality name |
| nation_team_id | national team_id on sofifa where the player plays |
| nation_position | player position in the national team |
| nation_jersey_number | player jersey number in the national team |
| preferred_foot | player preferred foot |
| weak_foot | player weak foot attribute |
| skill_moves | player skill moves attribute |
| international_reputation | player international reputation attribute |
| work_rate | player work rate attributes (attacking / defensive) |
| body_type | player body type |
| real_face | player real face |
| release_clause_eur | player release clause (in EUR) - if applicable |
| player_tags | player tags |
| player_traits | player traits |
| pace | player pace attribute |
| shooting | player shooting attribute |
| passing | player passing attribute |
| dribbling | player dribbling attribute |
| defending | player defending attribute |
| physic | player physic attribute |
| attacking_crossing | player crossing attribute |
| attacking_finishing | player finishing attribute |
| attacking_heading_accuracy | player heading accuracy attribute |
| attacking_short_passing | player short passing attribute |
| attacking_volleys | player volleys attribute |
| skill_dribbling | player dribbling attribute |
| skill_curve | player curve attribute |
| skill_fk_accuracy | player free-kick accuracy attribute |
| skill_long_passing | player long passing attribute |
| skill_ball_control | player ball control attribute |
| movement_acceleration | player acceleration attribute |
| movement_sprint_speed | player sprint speed attribute |
| movement_agility | player agility attribute |
| movement_reactions | player reactions attribute |
| movement_balance | player balance attribute |
| power_shot_power | player shot power attribute |
| power_jumping | player jumping attribute |
| power_stamina | player stamina attribute |
| power_strength | player strength attribute |
| power_long_shots | player long shots attribute |
| mentality_aggression | player aggression attribute |
| mentality_interceptions | player interceptions attribute |
| mentality_positioning | player positioning attribute |
| mentality_vision | player vision attribute |
| mentality_penalties | player penalties attribute |
| mentality_composure | player composure attribute |
| defending_marking_awareness | player marking awareness attribute |
| defending_standing_tackle | player standing tackle attribute |
| defending_sliding_tackle | player sliding tackle attribute |
| goalkeeping_diving | player GK diving attribute |
| goalkeeping_handling | player GK handling attribute |
| goalkeeping_kicking | player GK kicking attribute |
| goalkeeping_positioning | player GK positioning attribute |
| goalkeeping_reflexes | player GK reflexes attribute |
| goalkeeping_speed | player GK speed attribute |
| ls | player attribute playing as LS – left striker |
| st | player attribute playing as ST – striker, or centre forward |
| rs | player attribute playing as RS – right striker |
| lw | player attribute playing as LW – left winger |
| lf | player attribute playing as LF – left forward |
| cf | player attribute playing as CF – centre forward or central forward |
| rf | player attribute playing as RF – right forward |
| rw | player attribute playing as RW – right winger winger |
| lam | player attribute playing as LAM – left attacking midfielder |
| cam | player attribute playing as CAM – central attacking midfielder |
| ram | player attribute playing as RAM – right attacking midfielder |
| lm | player attribute playing as LM – left midfielder |
| lcm | player attribute playing as LCM – left central midfielder |
| cm | player attribute playing as CM – central midfielder |
| rcm | player attribute playing as RCM – right central midfielder |
| rm | player attribute playing as RM – right midfielder |
| lwb | player attribute playing as LWB – left wing back |
| ldm | player attribute playing as LDM - left defensive midfielder |
| cdm | player attribute playing as CDM – central defensive midfielder |
| rdm | player attribute playing as RDM – right defensive midfielder |
| rwb | player attribute playing as RWB – right wing back |
| lb | player attribute playing as LB – left back |
| lcb | player attribute playing as LCB – left central back |
| cb | player attribute playing as CB – central back |
| rcb | player attribute playing as RCB – right central back |
| rb | player attribute playing as RB – right back |
| gk | player attribute playing as GK – goalkeeper |
| player_face_url | URL of the player face |
| club_logo_url | URL of the club logo |
| club_flag_url | URL of the club nationality flag |
| nation_logo_url | URL of the national team logo |
| nation_flag_url | URL of the national flag |
**Um ponto bastante interessante e importante deste projeto experimental é que os dados sintéticos aqui apresentados podem ser coletados no mundo real. Basta verificar alguns dos atributos que serão utilizados com maiores detalhes. Veja as descrições aqui.
Para este projeto, apenas alguns atributos serão utilizados para a elaboração do produto de dados final. Portanto, as variáveis abaixo serão removidas do conjunto de dados visando melhorar a clareza, bem como, a o desempenho geral do computador utilizado.
fifas <- subset(fifas, select = -c( sofifa_id , player_url ,
club_team_id , club_name ,
league_name , league_level ,
club_loaned_from , club_joined ,
club_contract_valid_until , nationality_id ,
nationality_name , nation_team_id ,
nation_position , nation_jersey_number ,
real_face , release_clause_eur ,
player_tags , player_traits ,
player_face_url , club_logo_url ,
club_flag_url , nation_logo_url ,
nation_flag_url , international_reputation,
mentality_aggression , mentality_interceptions,
mentality_positioning , mentality_vision,
mentality_penalties , mentality_composure,
goalkeeping_speed , club_jersey_number,
work_rate , body_type,
ls , st,
rs , lw,
lf , cf,
rf , rw,
lam , cam,
ram , lm,
lcm , cm,
rcm , rm,
lwb , ldm,
cdm , rdm,
rwb , lb,
lcb , cb,
rcb , rb,
gk
))
Aqui, verifiquei a dimensão do data frame após concatenar todas as temporadas.
## A base de dados contém: 142079 linhas e 50 colunas.
A limpeza de dados compreende algumas etapas abrangentes para garantir a qualidade dos dados quando chegarmos à fase de modelagem. Estes são descritos abaixo:
# Create a data frame with column index, name, and data type
column_info <- data.frame(
Column_Index = 1:length(names(fifas)),
Data_Type = sapply(fifas, typeof),
stringsAsFactors = FALSE
)
# Print the table using kableExtra for styling
kable(column_info, align = "c") %>%
kable_styling(full_width = FALSE, bootstrap_options = "striped", font_size = 14) %>%
add_header_above(c(" " = 1, "Column Index" = 1, "Data Type" = 1), bold = TRUE) %>%
row_spec(0, background = "white",color = "black") %>%
column_spec(1:3, color = "black")
| Column_Index | Data_Type | |
|---|---|---|
| short_name | 1 | character |
| long_name | 2 | character |
| player_positions | 3 | character |
| overall | 4 | integer |
| potential | 5 | integer |
| value_eur | 6 | integer |
| wage_eur | 7 | integer |
| age | 8 | integer |
| dob | 9 | character |
| height_cm | 10 | integer |
| weight_kg | 11 | integer |
| club_position | 12 | character |
| preferred_foot | 13 | character |
| weak_foot | 14 | integer |
| skill_moves | 15 | integer |
| pace | 16 | integer |
| shooting | 17 | integer |
| passing | 18 | integer |
| dribbling | 19 | integer |
| defending | 20 | integer |
| physic | 21 | integer |
| attacking_crossing | 22 | integer |
| attacking_finishing | 23 | integer |
| attacking_heading_accuracy | 24 | integer |
| attacking_short_passing | 25 | integer |
| attacking_volleys | 26 | integer |
| skill_dribbling | 27 | integer |
| skill_curve | 28 | integer |
| skill_fk_accuracy | 29 | integer |
| skill_long_passing | 30 | integer |
| skill_ball_control | 31 | integer |
| movement_acceleration | 32 | integer |
| movement_sprint_speed | 33 | integer |
| movement_agility | 34 | integer |
| movement_reactions | 35 | integer |
| movement_balance | 36 | integer |
| power_shot_power | 37 | integer |
| power_jumping | 38 | integer |
| power_stamina | 39 | integer |
| power_strength | 40 | integer |
| power_long_shots | 41 | integer |
| defending_marking_awareness | 42 | integer |
| defending_standing_tackle | 43 | integer |
| defending_sliding_tackle | 44 | integer |
| goalkeeping_diving | 45 | integer |
| goalkeeping_handling | 46 | integer |
| goalkeeping_kicking | 47 | integer |
| goalkeeping_positioning | 48 | integer |
| goalkeeping_reflexes | 49 | integer |
| year | 50 | integer |
# Remove rows from club_position empty cells
fifass <- subset(fifas, club_position != "")
fifas <- fifass
rm(fifass)
na_count <- sapply(fifas, function(x) sum(is.na(x)))
# Create a list with variable name - value format
na_count_list <- paste(names(na_count), na_count, sep = ": ")
# Print the list
print(na_count_list)
## [1] "short_name: 0" "long_name: 0"
## [3] "player_positions: 0" "overall: 0"
## [5] "potential: 0" "value_eur: 275"
## [7] "wage_eur: 0" "age: 0"
## [9] "dob: 0" "height_cm: 0"
## [11] "weight_kg: 0" "club_position: 0"
## [13] "preferred_foot: 0" "weak_foot: 0"
## [15] "skill_moves: 0" "pace: 15543"
## [17] "shooting: 15543" "passing: 15543"
## [19] "dribbling: 15543" "defending: 15543"
## [21] "physic: 15543" "attacking_crossing: 0"
## [23] "attacking_finishing: 0" "attacking_heading_accuracy: 0"
## [25] "attacking_short_passing: 0" "attacking_volleys: 0"
## [27] "skill_dribbling: 0" "skill_curve: 0"
## [29] "skill_fk_accuracy: 0" "skill_long_passing: 0"
## [31] "skill_ball_control: 0" "movement_acceleration: 0"
## [33] "movement_sprint_speed: 0" "movement_agility: 0"
## [35] "movement_reactions: 0" "movement_balance: 0"
## [37] "power_shot_power: 0" "power_jumping: 0"
## [39] "power_stamina: 0" "power_strength: 0"
## [41] "power_long_shots: 0" "defending_marking_awareness: 0"
## [43] "defending_standing_tackle: 0" "defending_sliding_tackle: 0"
## [45] "goalkeeping_diving: 0" "goalkeeping_handling: 0"
## [47] "goalkeeping_kicking: 0" "goalkeeping_positioning: 0"
## [49] "goalkeeping_reflexes: 0" "year: 0"
A variável resposta do projeto é a “value_eur”, que apresenta 275 dados faltantes. Dentro do contexto deste projeto, decidi remover os dados faltantes, uma vez que se trata de um primeiro ciclo, com dados experimentais.
cat("O conjunto de dados original contém", dim(fifas)[1], " linhas e", dim(fifas)[2], "colunas.")
## O conjunto de dados original contém 140449 linhas e 50 colunas.
fifas <- fifas[complete.cases(fifas$value_eur), ]
cat("Após remoção dos dados faltantes da variável resposta, o conjunto de dados agora tem", dim(fifas)[1], " linhas e", dim(fifas)[2], "colunas.")
## Após remoção dos dados faltantes da variável resposta, o conjunto de dados agora tem 140174 linhas e 50 colunas.
Ainda, as variáveis “pace”, “shooting”, “passing”, “dribbling”, “defending”, e “physic” apresentam 15791 dados faltantes. Estes também serão removidos. Vale ressaltar que todos os dados faltantes são correspondentes nas linhas. Portanto, remover os dados faltantes da variável “pace” automaticamente removerá o restante.
fifas <- fifas[complete.cases(fifas$pace), ]
cat('Após remoção dos dados faltantes das variáveis "pace", "shooting", "passing", "dribbling", "defending", e "physic", o conjunto de dados agora tem', dim(fifas)[1], " linhas e", dim(fifas)[2], "colunas.")
## Após remoção dos dados faltantes das variáveis "pace", "shooting", "passing", "dribbling", "defending", e "physic", o conjunto de dados agora tem 124631 linhas e 50 colunas.
na_count <- sapply(fifas, function(x) sum(is.na(x)))
# Create a list with variable name - value format
na_count_list <- paste(names(na_count), na_count, sep = ": ")
# Print the list
print(na_count_list)
## [1] "short_name: 0" "long_name: 0"
## [3] "player_positions: 0" "overall: 0"
## [5] "potential: 0" "value_eur: 0"
## [7] "wage_eur: 0" "age: 0"
## [9] "dob: 0" "height_cm: 0"
## [11] "weight_kg: 0" "club_position: 0"
## [13] "preferred_foot: 0" "weak_foot: 0"
## [15] "skill_moves: 0" "pace: 0"
## [17] "shooting: 0" "passing: 0"
## [19] "dribbling: 0" "defending: 0"
## [21] "physic: 0" "attacking_crossing: 0"
## [23] "attacking_finishing: 0" "attacking_heading_accuracy: 0"
## [25] "attacking_short_passing: 0" "attacking_volleys: 0"
## [27] "skill_dribbling: 0" "skill_curve: 0"
## [29] "skill_fk_accuracy: 0" "skill_long_passing: 0"
## [31] "skill_ball_control: 0" "movement_acceleration: 0"
## [33] "movement_sprint_speed: 0" "movement_agility: 0"
## [35] "movement_reactions: 0" "movement_balance: 0"
## [37] "power_shot_power: 0" "power_jumping: 0"
## [39] "power_stamina: 0" "power_strength: 0"
## [41] "power_long_shots: 0" "defending_marking_awareness: 0"
## [43] "defending_standing_tackle: 0" "defending_sliding_tackle: 0"
## [45] "goalkeeping_diving: 0" "goalkeeping_handling: 0"
## [47] "goalkeeping_kicking: 0" "goalkeeping_positioning: 0"
## [49] "goalkeeping_reflexes: 0" "year: 0"
cat('A nova dimensão do conjunto de dados contém', dim(fifas)[1], "linhas e", dim(fifas)[2], "colunas.")
## A nova dimensão do conjunto de dados contém 124631 linhas e 50 colunas.
cat('')
Uma característica interessante na linguagem R é que ao chamar a função “head” para verificar as primeiras linhas do conjunto de dados, também é apresentado o tipo de variável. Tal característica auxilia na formatação do tipo de variável.
Abaixo, segue a formatação das variáveis.
fifas$dob <- as.Date(fifas$dob, format = "%Y-%m-%d")
head(fifas,3)
## short_name long_name player_positions
## 1 L. Messi Lionel Andrés Messi Cuccittini CF
## 2 Cristiano Ronaldo Cristiano Ronaldo dos Santos Aveiro LW, LM
## 3 A. Robben Arjen Robben RM, LM, RW
## overall potential value_eur wage_eur age dob height_cm weight_kg
## 1 93 95 100500000 550000 27 1987-06-24 169 67
## 2 92 92 79000000 375000 29 1985-02-05 185 80
## 3 90 90 54500000 275000 30 1984-01-23 180 80
## club_position preferred_foot weak_foot skill_moves pace shooting passing
## 1 CF Left 3 4 93 89 86
## 2 LW Right 4 5 93 93 81
## 3 SUB Left 2 4 93 86 83
## dribbling defending physic attacking_crossing attacking_finishing
## 1 96 27 63 84 94
## 2 91 32 79 83 95
## 3 92 32 64 80 85
## attacking_heading_accuracy attacking_short_passing attacking_volleys
## 1 71 89 85
## 2 86 82 87
## 3 50 86 86
## skill_dribbling skill_curve skill_fk_accuracy skill_long_passing
## 1 96 89 90 76
## 2 93 88 79 72
## 3 93 85 83 76
## skill_ball_control movement_acceleration movement_sprint_speed
## 1 96 96 90
## 2 92 91 94
## 3 90 93 93
## movement_agility movement_reactions movement_balance power_shot_power
## 1 94 94 95 80
## 2 93 90 63 94
## 3 93 89 91 86
## power_jumping power_stamina power_strength power_long_shots
## 1 73 77 60 88
## 2 94 89 79 93
## 3 61 78 65 90
## defending_marking_awareness defending_standing_tackle
## 1 25 21
## 2 22 31
## 3 29 26
## defending_sliding_tackle goalkeeping_diving goalkeeping_handling
## 1 20 6 11
## 2 23 7 11
## 3 26 10 8
## goalkeeping_kicking goalkeeping_positioning goalkeeping_reflexes year
## 1 15 14 8 2015
## 2 15 14 11 2015
## 3 11 5 15 2015
Para a realização da estatística descritiva geral, os dados serão separados em numéricos e não-numéricos.
No código abaixo, os dados numéricos serão separados dos não-numéricos:
options(scipen = 999)
df_num <- fifas[,sapply(fifas, is.numeric)]
round(t(describe(df_num)),2)
## overall potential value_eur wage_eur age height_cm
## vars 1.00 2.00 3.00 4.00 5.00 6.00
## n 124631.00 124631.00 124631.00 124631.00 124631.00 124631.00
## mean 65.86 70.92 2345896.36 11468.05 24.93 180.34
## sd 6.97 6.22 5850102.49 22705.80 4.49 6.44
## median 66.00 71.00 725000.00 4000.00 25.00 180.00
## trimmed 65.86 70.79 1147032.19 6530.99 24.73 180.35
## mad 7.41 5.93 733887.00 4447.80 4.45 7.41
## min 40.00 46.00 1000.00 500.00 16.00 154.00
## max 94.00 95.00 194000000.00 575000.00 39.00 204.00
## range 54.00 49.00 193999000.00 574500.00 23.00 50.00
## skew 0.04 0.18 8.35 6.49 0.35 -0.02
## kurtosis 0.03 0.04 111.43 75.96 -0.60 -0.21
## se 0.02 0.02 16571.07 64.32 0.01 0.02
## weight_kg weak_foot skill_moves pace shooting passing
## vars 7.00 8.00 9.00 10.00 11.00 12.00
## n 124631.00 124631.00 124631.00 124631.00 124631.00 124631.00
## mean 74.40 3.00 2.49 68.03 52.19 56.87
## sd 6.63 0.64 0.62 11.08 13.93 10.55
## median 74.00 3.00 2.00 69.00 54.00 58.00
## trimmed 74.28 2.98 2.41 68.53 52.66 57.16
## mad 5.93 0.00 0.00 10.38 14.83 10.38
## min 49.00 1.00 1.00 21.00 14.00 20.00
## max 110.00 5.00 5.00 97.00 94.00 93.00
## range 61.00 4.00 4.00 76.00 80.00 73.00
## skew 0.20 0.25 0.94 -0.53 -0.28 -0.23
## kurtosis 0.13 0.63 0.18 0.58 -0.75 -0.14
## se 0.02 0.00 0.00 0.03 0.04 0.03
## dribbling defending physic attacking_crossing attacking_finishing
## vars 13.00 14.00 15.00 16.00 17.00
## n 124631.00 124631.00 124631.00 124631.00 124631.00
## mean 62.03 50.98 64.81 53.97 49.55
## sd 10.39 16.69 9.74 14.04 16.30
## median 63.00 55.00 66.00 56.00 52.00
## trimmed 62.65 51.54 65.32 54.58 49.96
## mad 8.90 17.79 10.38 14.83 19.27
## min 22.00 14.00 27.00 11.00 10.00
## max 96.00 91.00 92.00 94.00 95.00
## range 74.00 77.00 65.00 83.00 85.00
## skew -0.56 -0.31 -0.46 -0.37 -0.22
## kurtosis 0.34 -1.12 -0.15 -0.55 -0.94
## se 0.03 0.05 0.03 0.04 0.05
## attacking_heading_accuracy attacking_short_passing attacking_volleys
## vars 18.00 19.00 20.00
## n 124631.00 124631.00 124631.00
## mean 56.87 62.18 46.74
## sd 11.63 10.03 14.73
## median 57.00 63.00 47.00
## trimmed 57.00 62.75 46.57
## mad 11.86 8.90 17.79
## min 12.00 20.00 10.00
## max 95.00 95.00 93.00
## range 83.00 75.00 83.00
## skew -0.10 -0.59 0.08
## kurtosis -0.34 0.74 -0.77
## se 0.03 0.03 0.04
## skill_dribbling skill_curve skill_fk_accuracy skill_long_passing
## vars 21.00 22.00 23.00 24.00
## n 124631.00 124631.00 124631.00 124631.00
## mean 60.18 51.20 46.48 55.80
## sd 12.59 15.03 14.99 12.48
## median 62.00 51.00 44.00 58.00
## trimmed 61.21 51.19 45.85 56.48
## mad 10.38 17.79 17.79 11.86
## min 12.00 11.00 10.00 15.00
## max 97.00 94.00 95.00 95.00
## range 85.00 83.00 85.00 80.00
## skew -0.75 0.00 0.33 -0.46
## kurtosis 0.55 -0.78 -0.77 -0.19
## se 0.04 0.04 0.04 0.04
## skill_ball_control movement_acceleration movement_sprint_speed
## vars 25.00 26.00 27.00
## n 124631.00 124631.00 124631.00
## mean 62.92 67.93 68.09
## sd 10.14 11.62 11.33
## median 64.00 69.00 69.00
## trimmed 63.48 68.49 68.63
## mad 8.90 10.38 10.38
## min 16.00 20.00 21.00
## max 96.00 97.00 97.00
## range 80.00 77.00 76.00
## skew -0.58 -0.57 -0.58
## kurtosis 0.74 0.60 0.70
## se 0.03 0.03 0.03
## movement_agility movement_reactions movement_balance power_shot_power
## vars 28.00 29.00 30.00 31.00
## n 124631.00 124631.00 124631.00 124631.00
## mean 66.26 61.90 66.45 59.60
## sd 12.35 9.00 12.15 13.33
## median 67.00 62.00 67.00 61.00
## trimmed 66.86 61.93 67.03 60.37
## mad 11.86 8.90 11.86 13.34
## min 21.00 24.00 17.00 11.00
## max 96.00 96.00 97.00 96.00
## range 75.00 72.00 80.00 85.00
## skew -0.47 -0.04 -0.49 -0.50
## kurtosis 0.14 -0.09 0.30 -0.15
## se 0.03 0.03 0.03 0.04
## power_jumping power_stamina power_strength power_long_shots
## vars 32.00 33.00 34.00 35.00
## n 124631.00 124631.00 124631.00 124631.00
## mean 65.84 67.16 65.68 51.33
## sd 11.64 11.26 12.66 15.72
## median 67.00 68.00 67.00 54.00
## trimmed 66.30 67.53 66.35 51.99
## mad 10.38 10.38 11.86 16.31
## min 23.00 20.00 19.00 11.00
## max 97.00 97.00 98.00 94.00
## range 74.00 77.00 79.00 83.00
## skew -0.42 -0.41 -0.47 -0.35
## kurtosis 0.29 0.38 0.06 -0.72
## se 0.03 0.03 0.04 0.04
## defending_marking_awareness defending_standing_tackle
## vars 36.00 37.00
## n 124631.00 124631.00
## mean 49.39 51.58
## sd 18.32 19.07
## median 54.00 58.00
## trimmed 50.16 52.64
## mad 19.27 17.79
## min 7.00 10.00
## max 94.00 94.00
## range 87.00 84.00
## skew -0.36 -0.47
## kurtosis -1.05 -1.03
## se 0.05 0.05
## defending_sliding_tackle goalkeeping_diving goalkeeping_handling
## vars 38.00 39.00 40.00
## n 124631.00 124631.00 124631.00
## mean 49.39 10.45 10.51
## sd 19.02 3.24 3.21
## median 55.00 10.00 10.00
## trimmed 50.26 10.43 10.50
## mad 19.27 4.45 4.45
## min 10.00 1.00 1.00
## max 95.00 75.00 75.00
## range 85.00 74.00 74.00
## skew -0.41 1.30 1.11
## kurtosis -1.10 19.26 16.67
## se 0.05 0.01 0.01
## goalkeeping_kicking goalkeeping_positioning goalkeeping_reflexes
## vars 41.00 42.00 43.00
## n 124631.00 124631.00 124631.00
## mean 10.51 10.47 10.46
## sd 3.28 3.21 3.25
## median 10.00 10.00 10.00
## trimmed 10.48 10.45 10.44
## mad 4.45 4.45 4.45
## min 1.00 1.00 1.00
## max 75.00 71.00 74.00
## range 74.00 70.00 73.00
## skew 1.48 1.11 1.35
## kurtosis 21.00 15.59 20.29
## se 0.01 0.01 0.01
## year
## vars 44.00
## n 124631.00
## mean 2018.65
## sd 2.28
## median 2019.00
## trimmed 2018.69
## mad 2.97
## min 2015.00
## max 2022.00
## range 7.00
## skew -0.08
## kurtosis -1.21
## se 0.01
df_num1 <- df_num[,1:22]
df_num2 <- df_num[,23:44]
# Melt the data frame to long format for plotting
df_num_melt1 <- melt(df_num1)
## No id variables; using all as measure variables
# Create a grid of histograms using ggplot2
ggplot(df_num_melt1, aes(x = value)) +
geom_histogram() +
facet_wrap(~ variable, nrow = 5, ncol = 5, scales = "free") +
labs(x = "Value", y = "Count")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
df_num1 <- df_num[,1:22]
df_num2 <- df_num[,23:44]
# Melt the data frame to long format for plotting
df_num_melt2 <- melt(df_num2)
## No id variables; using all as measure variables
# Create a grid of histograms using ggplot2
ggplot(df_num_melt2, aes(x = value)) +
geom_histogram() +
facet_wrap(~ variable, nrow = 5, ncol = 5, scales = "free") +
labs(x = "Value", y = "Count")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
df_num4 <- df_num[,1:11]
df_num5 <- df_num[,12:23]
df_num6 <- df_num[,24:35]
df_num7 <- df_num[,35:44]
# Melt the data frame to long format for plotting
df_num_melt4 <- melt(df_num4)
## No id variables; using all as measure variables
df_num_melt5 <- melt(df_num5)
## No id variables; using all as measure variables
df_num_melt6 <- melt(df_num6)
## No id variables; using all as measure variables
df_num_melt7 <- melt(df_num7)
## No id variables; using all as measure variables
ggplot(df_num_melt4, aes(sample = value)) +
geom_qq() +
facet_wrap(~ variable, nrow = 3, ncol = 5, scales = "free") +
labs(x = "Theoretical Quantiles", y = "Sample Quantiles")
ggplot(df_num_melt5, aes(sample = value)) +
geom_qq() +
facet_wrap(~ variable, nrow = 3, ncol = 5, scales = "free") +
labs(x = "Theoretical Quantiles", y = "Sample Quantiles")
ggplot(df_num_melt6, aes(sample = value)) +
geom_qq() +
facet_wrap(~ variable, nrow = 3, ncol = 5, scales = "free") +
labs(x = "Theoretical Quantiles", y = "Sample Quantiles")
ggplot(df_num_melt7, aes(sample = value)) +
geom_qq() +
facet_wrap(~ variable, nrow = 3, ncol = 5, scales = "free") +
labs(x = "Theoretical Quantiles", y = "Sample Quantiles")
# Removing unnecessary files
rm(df_num,df_num1,df_num2,df_num3,df_num4,df_num5,df_num6,df_num7,df_num_melt1,df_num_melt2,df_num_melt3,df_num_melt4,df_num_melt5,df_num_melt6,df_num_melt7, column_info)
## Warning in rm(df_num, df_num1, df_num2, df_num3, df_num4, df_num5, df_num6, :
## object 'df_num3' not found
## Warning in rm(df_num, df_num1, df_num2, df_num3, df_num4, df_num5, df_num6, :
## object 'df_num_melt3' not found
O conjunto de dados também possui algumas variáveis categoricas, que foram separadas das variáveis numéricas para análise de pré-processamento e limpeza.
# Separate the non-numerical attributes of the original data frame.
df_cat <- fifas[,!sapply(fifas, is.numeric)]
# Show the first few rows for initial visual inspection.
head(df_cat)
## short_name long_name player_positions
## 1 L. Messi Lionel Andrés Messi Cuccittini CF
## 2 Cristiano Ronaldo Cristiano Ronaldo dos Santos Aveiro LW, LM
## 3 A. Robben Arjen Robben RM, LM, RW
## 4 Z. Ibrahimović Zlatan Ibrahimović ST
## 6 Iniesta Andrés Iniesta Luján CM, LW
## 7 L. Suárez Luis Alberto Suárez Díaz ST, CF
## dob club_position preferred_foot
## 1 1987-06-24 CF Left
## 2 1985-02-05 LW Right
## 3 1984-01-23 SUB Left
## 4 1981-10-03 ST Right
## 6 1984-05-11 LCM Right
## 7 1987-01-24 RES Right
Note que as variáveis categóricas são apenas 6 (eu um universo de 51 variáveis).
A base de dado possui uma variável chamada “player_positions”, que apresenta quais posições em campo o jogador pode atuar. Por exemplo, o jogador Luis Alberto Suárez Díaz pode atuar em duas posições: ST, CF. Por outro lado, existem jogadores que atuam em uma posição, ao passo que outros, em duas ou três. Portanto, criei uma variável que apresenta a contagem das possibilidades de posições em que o jogador pode participar para verificar o impacto desta versatilidade no valor final do jogador.
# CREATE A PLAYING POSITION COUNTING VARIABLE. THIS ONE WILL SHOW IN HOW MANY POSITIONS A PLAYER PLAYS.
fifas$p_position_count <- str_count(fifas$player_positions, ",") + 1
Este projeto visa estimar o valor de mercado de um jogador de futebol a partir de características facilmente quantificáveis. A base de dados disponível permite criar algumas hipóteses que serão ou não validadas para a elaboração do modelo de predição.
H1. Valor médio do jogador depende da posição que o mesmo atua; H2. Jogadores apresentam características distintas, de acordo com a posição que atuam; H3. Jogadores versáteis são mais valiosos; H4. Jogadores versáteis, dentro de sua faixa de atuação, são mais valiosos.
A filtragem de variáveis também faz parte da limpeza de dados. Algumas restrições que o negócio apresenta podem dificultar a implantação do modelo em produção. Portanto, filtrar as variáveis não apenas moldará os dados de acordo com a questão de negócio, mas também ajudará a escolher as variáveis mais relevantes para a modelagem.
Depois de um minucioso processo de limpeza que usou etapas específicas para preparar os dados, é hora de verificar como e a força do impacto dessas variáveis sobre os fenômenos que estou investigando. Nesta fase, é possível detectar nuances nos dados que possam influenciar a modelagem do aprendizado de máquina nas próximas etapas. Essa abordagem diminui substancialmente a tentativa e erro. Aqui, eu expandiria a experiência de negócios e seria capaz de gerar insights, seja trazendo novas informações para a mesa ou contrastando algo que se acreditava ser verdade. Finalmente, a EDA pode revelar variáveis importantes no modelo.
H1. Valor médio do jogador depende da posição que o mesmo atua:
CORRETO - Jogadores possuem valor médio de mercado distinto, de acordo
com sua posição de atuação em campo.
Aqui, primeiro criei um data frame com os agrupamentos e ordenamentos necessários para verificar as H1.
# Specify the name of your dataframe
df_num_cp <- fifas %>%
select(where(is.numeric), club_position)
df_num_cp$long_name <- NULL
# Calculate mean by grouping variables
mean_by_group <- aggregate(. ~ club_position, data = df_num_cp, FUN = mean, na.rm = TRUE)
# Sort from value_eur
mean_by_group <- mean_by_group %>%
arrange(desc(value_eur))
mean_by_group <- subset(mean_by_group, select = c("club_position", "value_eur"))
# Calculate SD by grouping variables
sd_by_group <- aggregate(. ~ club_position, data = df_num_cp, FUN = sd, na.rm = TRUE)
# Sort from value_eur
sd_by_group <- sd_by_group %>%
arrange(desc(value_eur))
sd_by_group <- subset(sd_by_group, select = c("club_position", "value_eur"))
# Merge mean and sd data
mean_val_eur <- merge(mean_by_group, sd_by_group, by = "club_position")
mean_val_eur <- mean_val_eur %>%
arrange(desc(value_eur.x))
mean_val_eur$cv <- (mean_val_eur$value_eur.y/mean_val_eur$value_eur.x) * 100
colnames(mean_val_eur) <- c("club_position", "value_eur_avg", "value_eur_sd", "value_eur_cv")
# View the resulting data frame using kableExtra for styling
kable(mean_val_eur, align = "c") %>%
kable_styling(full_width = FALSE, bootstrap_options = "striped", font_size = 14) %>%
add_header_above(c("Club Position" = 1, "Valor Mercado (média)" = 1, "Valor Mercado (desvio-padrão)" = 1, "Coeficiente de variacao(%)" = 1), bold = TRUE, color = "black") %>%
row_spec(1, background = "white",color = "black") %>%
column_spec(1:4, color = "black")
| club_position | value_eur_avg | value_eur_sd | value_eur_cv |
|---|---|---|---|
| CF | 10557555.6 | 19256219 | 182.3928 |
| LW | 6532372.7 | 15723222 | 240.6969 |
| RW | 6119948.2 | 13313717 | 217.5462 |
| LF | 4746666.7 | 8051763 | 169.6298 |
| ST | 4728807.7 | 10550463 | 223.1104 |
| CAM | 4564981.5 | 8851036 | 193.8899 |
| RAM | 4437762.6 | 6819246 | 153.6641 |
| CDM | 4190715.1 | 8895817 | 212.2744 |
| RF | 4165277.8 | 7504344 | 180.1643 |
| LS | 4037059.5 | 9255610 | 229.2661 |
| LAM | 4005068.2 | 6267030 | 156.4775 |
| RCM | 3816270.1 | 8942462 | 234.3247 |
| LCM | 3800055.7 | 8194705 | 215.6470 |
| RS | 3679727.5 | 8499348 | 230.9776 |
| LM | 3535750.2 | 7341573 | 207.6383 |
| RM | 3403197.6 | 6769326 | 198.9108 |
| RDM | 3216514.2 | 7142487 | 222.0568 |
| LDM | 3132473.4 | 6396972 | 204.2147 |
| CM | 3072530.1 | 5827588 | 189.6674 |
| RCB | 2951151.9 | 6159733 | 208.7230 |
| CB | 2890418.3 | 5814931 | 201.1796 |
| LCB | 2818676.2 | 6305563 | 223.7065 |
| LB | 2449315.9 | 5501541 | 224.6154 |
| RB | 2381714.7 | 5132908 | 215.5132 |
| RWB | 2370266.3 | 4492949 | 189.5546 |
| LWB | 2054492.8 | 3673914 | 178.8234 |
| SUB | 1834380.5 | 4052681 | 220.9291 |
| RES | 784039.3 | 1734567 | 221.2348 |
H2. Jogadores apresentam características distintas, de acordo com a
posição que atuam:
CORRETO - Jogadores apresentam características específicas que otimizam
a função que cumprem.
# Specify the name of your dataframe
df_h2 <- fifas %>%
select(where(is.numeric), club_position)
df_h2$long_name <- NULL
# Calculate mean by grouping variables
h2_mean_by_group <- aggregate(. ~ club_position, data = df_h2, FUN = mean, na.rm = TRUE)
# Sort from value_eur
h2_mean_by_group <- h2_mean_by_group %>%
arrange(desc(value_eur))
# Remove the 'club_position' column from the data frame
h2_df_without_cp <- h2_mean_by_group[, !(names(h2_mean_by_group) %in% "club_position")]
# Iterate over each column and create a bar chart
for (column in names(h2_df_without_cp)) {
p <- ggplot(h2_mean_by_group, aes(x = reorder(club_position, .data[[column]]), y = .data[[column]])) +
geom_col(fill = "blue") +
labs(x = "Club Position", y = column) +
ggtitle(paste("Column Chart -", column)) +
coord_flip()
print(p)
}
H3. Jogadores versáteis são mais valiosos:
# Format scatterplot
p <- ggplot(fifas, aes(x = p_position_count, y = value_eur)) +
geom_point() +
labs(x = "Versatilidade", y = "Valor de Mercado")
# Print the plot
print(p)
Jogadores de futebol apresentam características distintas, que permitem que estes executem suas funções em campo;
Existe uma distinção de valor de mercado, de acordo com a posição que o jogador atua em campo;
As características dos jogadores são posição-dependente. Desta forma, jogadores que atuam na zona de ataque do campo possuem características distintas daqueles que atuam em zona de defesa.
Portanto, nossa orientação para o cliente é mensurar os indicadores de ataque, meio de campo, defesa, etc. e alocar o jogador em uma posição que seja otimizada por essas características.
Para o próximo ciclo de análise, será realizado uma análize, utilizando modelagem linear mista (linear mixed modelling), uma vez que os dados utilizados apresentam característica hierárquica, bem como fere premissas de modelos de regressão linear. Por fim, será criado um dashboard dinâmino na aplicação R Shiny, onde os stakeholders poderão acessar de qualquer equipamento com acesso a internet e tomas suas decisãoes, baseado nos dados apresentados.