Neste post, eu mostro como:
- Baixar dados de indicadores macroecômicos de todos os países usando a API do World Bank;
- Clusterizar países de acordo com esses indicadores usando o algoritmo k-means;
- O Brasil está mais próximo de Serra Leoa e Zimbábue que dos Estados Unidos e Noruega
library(WDI) # baixar os dados do World Bank
library(magrittr)
library(formattable)
Importação dos dados
Felizmente, o processo de importação dos dados do World Bank é feito de maneira automatizada pelo pacote WDI
usando a função WDI()
. Como é necessário inserir o código do indicador, usei a função WDIsearch()
para buscar o código do indicador relacionado a, por exemplo, inflação:
WDIsearch("Inflation")
## indicator name
## [1,] "FP.CPI.TOTL.ZG" "Inflation, consumer prices (annual %)"
## [2,] "NY.GDP.DEFL.KD.ZG" "Inflation, GDP deflator (annual %)"
Portanto, o código do indicador de inflação é “FP.CPI.TOTL.ZG”. Repeti o mesmo para outros indicadores que escolhi para esta análise:
# lista de indicadores para baixar:
lista_indicadores <- c("FP.CPI.TOTL.ZG", # inflação (%)
"NY.GDP.PCAP.CD", # Pib per capita (USD)
"NY.GDP.MKTP.KD.ZG", # crescimento do PIB anual (%),
"SL.UEM.TOTL.ZS" # Desemprego (%)
)
# Usei 2014 como ano de referência pois os resultados de alguns indicadores de 2015 ainda não foram disponibilizados
df <- WDI(indicator = lista_indicadores, country = "all", start = 2014, end = 2014, extra = TRUE)
str(df)
## 'data.frame': 264 obs. of 14 variables:
## $ iso2c : chr "1A" "1W" "4E" "7E" ...
## $ country : chr "Arab World" "World" "East Asia & Pacific (excluding high income)" "Europe & Central Asia (excluding high income)" ...
## $ year : num 2014 2014 2014 2014 2014 ...
## $ FP.CPI.TOTL.ZG : num 2.67 2.51 3.86 2.53 6.82 ...
## $ NY.GDP.PCAP.CD : num 7446 10850 6305 9888 1497 ...
## $ NY.GDP.MKTP.KD.ZG: num 2.91 2.83 6.76 2.18 7.06 ...
## $ SL.UEM.TOTL.ZS : num 11.49 5.76 4.39 7.71 3.88 ...
## $ iso3c : Factor w/ 248 levels "ABW","AFG","AGO",..: 6 243 NA NA 195 5 7 2 11 4 ...
## $ region : Factor w/ 8 levels "Aggregates","East Asia & Pacific (all income levels)",..: 1 1 NA NA 1 3 5 7 4 3 ...
## $ capital : Factor w/ 211 levels "","Abu Dhabi",..: 1 1 NA NA 1 10 2 80 167 191 ...
## $ longitude : Factor w/ 211 levels "","-0.126236",..: 1 1 NA NA 1 45 141 169 158 72 ...
## $ latitude : Factor w/ 211 levels "","0.20618","-0.229498",..: 1 1 NA NA 1 137 77 105 46 131 ...
## $ income : Factor w/ 7 levels "Aggregates","High income: nonOECD",..: 1 1 NA NA 1 2 2 5 7 7 ...
## $ lending : Factor w/ 5 levels "Aggregates","Blend",..: 1 1 NA NA 1 5 5 4 3 3 ...
O output acima mostra que o data frame não contém dados apenas de países mas também de unidades agregadas, como o mundo, mundo árabe, América Latina, etc. Por isso, removi as unidades agregadas:
df$region %<>% as.character
# remover agregados
df <- subset(df, region != "Aggregates")
Abaixo eu crio um novo dataframe apenas com as variáveis de interesse:
df2 <- df[, lista_indicadores]
row.names(df2) <- df$country
colnames(df2) <- c("inflacao", "pib_per_capita", "crescimento_pib", "desemprego")
summary(df2)
## inflacao pib_per_capita crescimento_pib desemprego
## Min. :-1.5092 Min. : 312.8 Min. :-6.553 Min. : 0.100
## 1st Qu.: 0.6085 1st Qu.: 1937.1 1st Qu.: 1.529 1st Qu.: 4.748
## Median : 2.6378 Median : 6234.4 Median : 3.284 Median : 6.859
## Mean : 3.9875 Mean : 16021.2 Mean : 3.374 Mean : 9.105
## 3rd Qu.: 5.3502 3rd Qu.: 18697.0 3rd Qu.: 5.227 3rd Qu.:11.818
## Max. :62.1686 Max. :179478.6 Max. :10.300 Max. :31.334
## NA's :34 NA's :19 NA's :21 NA's :27
Duas observações importantes sobre o output acima:
- Para facilitar a interpretação dos resultados da análise, transformei a taxa de desemprego em taxa de emprego, pois assim temos três indicadores que. quanto maior seus valores, mais pujante é a Economia de seus países;
- Alguns países não contém dados para alguns dos indicadores. Não há informação, por exemplo, sobre desemprego em 38 países.
Para resolver o problema dos valores ausentes (os NA
), poderia ser aplicada uma técnica robusta, mas como esta é uma análise simples ou optei por remover os países que tinham algum dado faltando.
df2 <- na.omit(df2)
df2$desemprego <- 100 - df2$desemprego
names(df2)[4] <- "emprego"
Clusterização
Para usar o algoritmo k-means para clusterizar os países, é necessário:
- Calcular a distância (dissimilaridade) entre os países;
- Escolher o número de clusteres.
Para o cálculo da distância, temos um problema: as escalas das colunas são diferentes. Enquanto o PIB per capita é dado em dólares por pessoa e vão de 255 a 116,613, os outros são dados em porcentagem. Se não for feita nenhuma transformação dos dados, o PIB per capita terá um peso muito maior na clusterização dos dados que os outros indicadores.
Por isso, é necessário convertes todos os indicadores a uma escala única de média 0:
df2_escala <- scale(df2)
# Conferindo o output para o Brasil
df2_escala["Brazil", ]
## inflacao pib_per_capita crescimento_pib emprego
## 0.5197757 -0.1461311 -1.1241216 0.3173180
Na nova escala, temos que o Brasil apresenta inflação acima da média, PIB per capita abaixo da média, Crescimento do PIB abaixo da média (e olha que isso foi em 2014…) e taxa de emprego acima da média.
A determinação da quantidade de clusteres não segue uma regra pré-definida e deve ser pensada pelo responsável pela análise. Cada projeto de clusterização tem suas próprias particularidades. Contudo, alguns métodos analíticos podem ajudar nessa escolha, seja pela minização da soma dos quadrados dos clusteres ou pelo auxílio visual de um dendograma.
Para determinar o número de clusteres pelo primeiro método, observe o gráfico abaixo:
# referencia: http://www.statmethods.net/advstats/cluster.html
wss <- (nrow(df2_escala)-1)*sum(apply(df2_escala,2,var))
for (i in 2:15) wss[i] <- sum(kmeans(df2_escala,
centers=i)$withinss)
plot(1:15, wss, type="b", xlab="Número of Clusters",
ylab="Soma dos quadrados dentro dos clusteres")
A soma dos quadrados dos clusteres se mantem estável a partir de 8 segmentos. Contudo, é preciso pensar qual a interpretação que teríamos disso. Quer dizer, posso dizer que dividi os dados em 8 clusteres, mas… e daí? O que seria aprendido por meio desses 8 clusteres?
Pelo segundo método, um dendograma é criado:
dendo <- df2_escala %>% dist %>% hclust
plot(dendo)
rect.hclust(dendo, k = 3, border = "blue")
rect.hclust(dendo, k = 4, border = "red")
A posição de cada país no dendograma é determinada pela dissimilaridade entre cada um dos outros países. Veja que a opção de 4 segmentos divide um dos segmentos da opção de 3 ao meio. Portanto, 4 parece ser uma boa escolha para a quantidade de clusteres do modelo desta análise.
Por exemplo, esta é a distância entre o Brasil e alguns outros países:
df2_escala[c("Brazil", "Sierra Leone", "Zimbabwe", "Norway", "United States"),] %>% dist
## Brazil Sierra Leone Zimbabwe Norway
## Sierra Leone 1.787662
## Zimbabwe 1.695590 1.258069
## Norway 4.101143 4.610387 4.490002
## United States 2.312514 2.753254 2.520473 2.014109
Dá para ver que o Brasil tem uma distância euclidiana de 1,90 em relação a Serra Leoa, 2,06 ao Zimbábue, 2,33 aos Estados Unidos e 4,06 a Noruega. Ou seja, levando em conta os indicadores macroeconômicos considerados nesta análise, é possível dizer que o Brasil é mais similar com países miseráveis do que com países ricos (Veja como os EUA são menos distantes em relação a Noruega do que ao Zimbábue).
Podemos também ver qual a distribuição do grau de dissimularidade do Brasil com o resto do mundo:
mat_brasil <- df2_escala %>% dist(diag = TRUE, upper = TRUE) %>% as.matrix
# 5 países com menor dissimilaridade
mat_brasil[, "Brazil"] %>% sort() %>% head(6)
## Brazil Russian Federation Suriname
## 0.0000000 0.4111801 0.6143772
## Chile Equatorial Guinea Afghanistan
## 0.6877354 0.7004780 0.7478158
# 5 países com MAIOR dissimilaridade
mat_brasil[, "Brazil"] %>% sort() %>% tail(5)
## Namibia Norway Malawi Luxembourg Sudan
## 4.058707 4.101143 4.129854 5.473961 6.380094
O resultado dos 5 países mais distantes do Brasil é curioso: dentre eles, há 2 países ricos (Qatar e Luxemburgo) e três pobres (Malawi, Mauritânia e Sudão). Ou seja, não é necessariamente verdade que o Brasil é mais similar a países pobres da África que países ricos. Esse é o tipo de coisa que, se eu fosse um jornalista sensacionalista, omitiria.
Brincadeiras a parte, já podemos pular para a parte de criar os segmentos:
# fixar uma seed para garantir a reproducibilidade da análise:
set.seed(123)
# criar os clusteres
lista_clusteres <- kmeans(df2_escala, centers = 4)$cluster
# função customizada para calcular a média dos indicadores para cada cluster
cluster.summary <- function(data, groups) {
x <- round(aggregate(data, list(groups), mean), 2)
x$qtd <- as.numeric(table(groups))
# colocar coluna de quantidade na segunda posição
x <- x[, c(1, 6, 2, 3, 4, 5)]
return(x)
}
(tabela <- cluster.summary(df2, lista_clusteres))
## Group.1 qtd inflacao pib_per_capita crescimento_pib emprego
## 1 1 97 3.62 6046.01 4.57 93.81
## 2 2 7 21.29 3026.91 1.85 92.65
## 3 3 30 1.74 54577.23 1.89 94.34
## 4 4 34 2.45 8977.34 1.76 79.76
Para melhorar a apresentação visual do output acima, usei o pacote formattable
junto com uma função que criei para colorir de verde o valor caso seja superior ou igual à média do indicador e vermelho caso contrário.
colorir.valor <- function(x) ifelse(x >= mean(x), style(color = "green"), style(color = "red"))
nome_colunas <- c("Cluster", "Quantidade de países do cluster", "Taxa de Inflação (%)",
"PIB Per Capita (US$)","Crescimento anual do PIB (%)", "Taxa de Emprego (%)")
formattable(
tabela,
list(
pib_per_capita = formatter("span", style = x ~ colorir.valor(x)),
crescimento_pib = formatter("span", style = x ~ colorir.valor(x)),
emprego = formatter("span", style = x ~ colorir.valor(x))
), col.names = nome_colunas, format = "markdown", pad = 0
)
Cluster | Quantidade de países do cluster | Taxa de Inflação (%) | PIB Per Capita (US$) | Crescimento anual do PIB (%) | Taxa de Emprego (%) |
---|---|---|---|---|---|
1 | 97 | 3.62 | 6046.01 | 4.57 | 93.81 |
2 | 7 | 21.29 | 3026.91 | 1.85 | 92.65 |
3 | 30 | 1.74 | 54577.23 | 1.89 | 94.34 |
4 | 34 | 2.45 | 8977.34 | 1.76 | 79.76 |
Temos, então, 4 grupos de países distintos:
- Cluster 1: Inflação acima da média, PIB per capita abaixo, crescimento acima, emprego acima: países em desenvolvimento;
- Cluster 2: Inflação abaixo da média, PIB per capita muito acima, crescimento abaixo, emprego acima: países ricos;
- Cluster 3: Inflação abaixo da média, PIB per capita abaixo, crescimento abaixo, semprego acima: países relativamente pobres, piores que os do Cluster 1;
- Cluster 4: Inflação abaixo da média, PIB per capita abaixo, crescimento abaixo, emprego muito abaixo: países pobres.
Para finalizar, qual é o cluster do Brasil e quais os outros países que estão no mesmo segmento?
df2$cluster <- lista_clusteres
df2["Brazil",]
## inflacao pib_per_capita crescimento_pib emprego cluster
## Brazil 6.332092 12026.62 0.5039618 93.188 1
cl_brasil <- df2["Brazil", ]$cluster
x <- df2[df2$cluster == cl_brasil, ]
x[order(-x$pib_per_capita),] %>% knitr::kable()
inflacao | pib_per_capita | crescimento_pib | emprego | cluster | |
---|---|---|---|---|---|
Korea, Rep. | 1.2747997 | 27811.3664 | 3.3414478 | 96.500 | 1 |
Malta | 0.3115001 | 26180.9260 | 8.3056338 | 94.196 | 1 |
Bahrain | 2.6511955 | 24983.3790 | 4.3498423 | 98.811 | 1 |
Saudi Arabia | 2.6705256 | 24575.4030 | 3.6524817 | 94.280 | 1 |
Slovenia | 0.2000749 | 24020.6729 | 3.1062802 | 90.332 | 1 |
Estonia | -0.1448155 | 19941.4553 | 2.8229070 | 92.648 | 1 |
Czech Republic | 0.3371869 | 19744.5586 | 2.7151161 | 93.892 | 1 |
Uruguay | 8.8773533 | 16737.8983 | 3.2387912 | 93.540 | 1 |
Lithuania | 0.1037899 | 16554.9714 | 3.4950162 | 89.302 | 1 |
Latvia | 0.6085193 | 15725.0137 | 2.1199188 | 89.154 | 1 |
Chile | 4.3950000 | 14817.3778 | 1.9096934 | 93.610 | 1 |
Poland | 0.1069519 | 14341.6705 | 3.2825701 | 91.010 | 1 |
Russian Federation | 7.8298397 | 14125.9061 | 0.7314582 | 94.844 | 1 |
Hungary | -0.2223151 | 14117.9767 | 4.0473212 | 92.275 | 1 |
Kazakhstan | 6.7183066 | 12806.5651 | 4.2000000 | 94.939 | 1 |
Panama | 2.6378294 | 12593.7370 | 6.0533341 | 95.180 | 1 |
Turkey | 8.8545727 | 12127.2252 | 5.1666907 | 90.120 | 1 |
Brazil | 6.3320923 | 12026.6173 | 0.5039618 | 93.188 | 1 |
Malaysia | 3.1746032 | 11183.9619 | 6.0121665 | 97.130 | 1 |
Costa Rica | 4.5153127 | 10647.4418 | 3.6568036 | 90.380 | 1 |
Mexico | 4.0186172 | 10452.2777 | 2.2653339 | 95.169 | 1 |
Mauritius | 3.2176877 | 10153.9382 | 3.7445744 | 92.281 | 1 |
Romania | 1.0689610 | 10020.2773 | 3.0763023 | 93.198 | 1 |
Suriname | 3.3897457 | 9564.4064 | 0.3636006 | 93.060 | 1 |
Lebanon | 0.7497186 | 8161.4614 | 1.8000000 | 93.770 | 1 |
Colombia | 2.8778103 | 7913.3834 | 4.3936083 | 90.848 | 1 |
Azerbaijan | 1.3850289 | 7891.2998 | 2.0000000 | 95.090 | 1 |
Maldives | 2.1201131 | 7716.2040 | 5.9973264 | 94.790 | 1 |
China | 2.0003449 | 7683.5020 | 7.2976660 | 95.407 | 1 |
Peru | 3.2260468 | 6491.0525 | 2.3543330 | 95.924 | 1 |
Ecuador | 3.5731279 | 6432.2165 | 3.9927085 | 96.200 | 1 |
Dominican Republic | 2.9986423 | 6268.6921 | 7.6089648 | 85.500 | 1 |
Thailand | 1.8958901 | 5941.8407 | 0.9145191 | 99.160 | 1 |
Algeria | 2.9164064 | 5470.8510 | 3.7891212 | 89.400 | 1 |
Fiji | 0.5403289 | 5046.0373 | 5.4464694 | 91.085 | 1 |
Belize | 1.2013996 | 4852.2237 | 4.0810340 | 88.400 | 1 |
Paraguay | 5.0288277 | 4712.8227 | 4.7223337 | 93.990 | 1 |
Angola | 7.2795615 | 4709.3120 | 4.8044727 | 93.196 | 1 |
Georgia | 3.0688121 | 4429.6501 | 4.6233317 | 87.650 | 1 |
Tonga | 2.5108763 | 4192.3498 | 2.0646846 | 94.924 | 1 |
Mongolia | 13.0246481 | 4181.5833 | 7.8852258 | 92.055 | 1 |
Samoa | -0.4068161 | 4178.9734 | 1.1961473 | 91.280 | 1 |
Jordan | 2.8915663 | 4066.9408 | 3.0963303 | 88.100 | 1 |
Guyana | 0.9207697 | 4030.8023 | 3.8409153 | 88.173 | 1 |
El Salvador | 1.1057751 | 3988.7719 | 1.4254757 | 94.079 | 1 |
Sri Lanka | 2.7632866 | 3820.5410 | 4.9607192 | 95.600 | 1 |
Guatemala | 3.4183617 | 3687.7638 | 4.1744006 | 97.090 | 1 |
Indonesia | 6.3949254 | 3491.5959 | 5.0066684 | 94.060 | 1 |
Egypt, Arab Rep. | 10.1458006 | 3327.7542 | 2.9159119 | 86.830 | 1 |
Nigeria | 8.0573826 | 3221.6781 | 6.3097183 | 95.200 | 1 |
Morocco | 0.4354565 | 3154.5135 | 2.5511096 | 90.100 | 1 |
Vanuatu | 0.7988638 | 3148.3651 | 2.3310062 | 94.703 | 1 |
Bolivia | 5.7835637 | 3124.0003 | 5.4605698 | 96.500 | 1 |
Congo, Rep. | 0.0771605 | 2910.5202 | 6.7799410 | 89.944 | 1 |
Philippines | 4.1044776 | 2842.9384 | 6.1452988 | 93.410 | 1 |
Bhutan | 8.2065451 | 2522.7960 | 5.7454552 | 97.370 | 1 |
Moldova | 5.0887858 | 2244.7638 | 4.7999997 | 96.140 | 1 |
Honduras | 6.1292493 | 2242.7119 | 3.0580806 | 94.500 | 1 |
Papua New Guinea | 5.2079395 | 2182.7166 | 8.5339018 | 97.420 | 1 |
Vietnam | 4.0858999 | 2052.3191 | 5.9836546 | 98.130 | 1 |
Lao PDR | 4.1352264 | 2017.5633 | 7.6135165 | 98.672 | 1 |
Nicaragua | 6.0357919 | 1975.4647 | 4.7854601 | 94.727 | 1 |
Sao Tome and Principe | 6.9984994 | 1821.8787 | 6.2076817 | 86.528 | 1 |
Djibouti | 1.3418649 | 1740.9150 | 6.0001943 | 93.439 | 1 |
Zambia | 7.8068755 | 1738.0882 | 4.6958264 | 92.274 | 1 |
India | 6.6495002 | 1573.1181 | 7.5052202 | 96.470 | 1 |
Cote d’Ivoire | 0.4530305 | 1569.9283 | 8.7940774 | 90.623 | 1 |
Cameroon | 1.9479483 | 1441.1401 | 5.9269650 | 95.902 | 1 |
Kenya | 6.8774981 | 1335.0646 | 5.3518399 | 88.185 | 1 |
Mauritania | 3.5352189 | 1326.6688 | 5.5795439 | 89.933 | 1 |
Pakistan | 7.1916712 | 1316.9810 | 4.6747080 | 94.400 | 1 |
Kyrgyz Republic | 7.5342473 | 1279.7698 | 4.0240386 | 91.950 | 1 |
Myanmar | 5.4744647 | 1262.8938 | 7.9912433 | 99.200 | 1 |
Timor-Leste | 0.4435484 | 1153.5157 | 5.8611362 | 96.864 | 1 |
Tajikistan | 6.1044277 | 1104.4590 | 6.6992507 | 89.268 | 1 |
Cambodia | 3.8552386 | 1098.6871 | 7.0715254 | 99.900 | 1 |
Bangladesh | 6.9911653 | 1084.5654 | 6.0610931 | 95.761 | 1 |
Senegal | -1.0797448 | 1052.4439 | 4.3110510 | 89.637 | 1 |
Zimbabwe | -0.2172862 | 1027.4075 | 2.7652707 | 94.869 | 1 |
Chad | 1.6808359 | 1025.9985 | 6.8999850 | 94.228 | 1 |
Tanzania | 6.1316143 | 950.3743 | 6.9651333 | 97.900 | 1 |
Benin | -1.0857445 | 943.6866 | 6.3575210 | 98.957 | 1 |
Mali | 0.8950092 | 825.5730 | 7.0433562 | 91.800 | 1 |
Uganda | 3.0762851 | 719.1727 | 5.2468190 | 98.088 | 1 |
Sierra Leone | 4.6454620 | 708.4395 | 4.5567724 | 97.200 | 1 |
Rwanda | 1.7841004 | 706.5700 | 7.6196102 | 96.591 | 1 |
Nepal | 8.3679788 | 706.2387 | 5.9889847 | 97.000 | 1 |
Burkina Faso | -0.2580895 | 705.1464 | 4.1859447 | 96.731 | 1 |
Guinea-Bissau | -1.5092446 | 642.6256 | 2.5410751 | 93.441 | 1 |
Togo | 0.1867161 | 620.1318 | 5.8717263 | 93.183 | 1 |
Afghanistan | 4.6043340 | 612.0697 | 1.3125309 | 91.551 | 1 |
Ethiopia | 7.3918145 | 571.1623 | 10.2574930 | 95.019 | 1 |
Guinea | 9.7139773 | 561.0997 | 0.3999986 | 93.048 | 1 |
Liberia | 9.8263580 | 458.4652 | 0.7011416 | 96.377 | 1 |
Madagascar | 6.0805955 | 452.4632 | 3.3158541 | 98.554 | 1 |
Niger | -0.9245447 | 430.6046 | 7.0497979 | 97.534 | 1 |
Burundi | 4.3798400 | 312.7490 | 4.6609182 | 98.432 | 1 |
Dá para perceber que existe um problema com nosso resultado: No mesmo segmento, estão presentes a Coreia do Sul e países como Haiti e Zimbábue. Isso pode ser explicado por uma série de razões, como:
- O número e perfil dos indicadores macroeconômicos escolhidos não é bom o suficiente para determinar uma segmentação eficiente dos países;
- O número de clusteres deveria ser maior;
- Deveriam ser feitas apenas interações (escolhendo valores diferentes como argumento de set.seed()
) - O erro se deve a um erro aleatório, também chamado de ruído, do algoritmo k-means. Afinal de contas, como sabemos, nenhum modelo é perfeito.