satisfacoes = read_csv("https://raw.githubusercontent.com/cienciadedados-ufcg/inferencia-ismir/master/data/satisfacoes.csv",
col_types = "cdcc")Os dados fonte desse documento são dados sobre a preferência de usuários quanto ao tipo de mêcanismo usado para a montagem de uma playlist em Jukeboxes. Foram dados carregados de um CSV que pode ser encontrados nesse link. Para mais referencias, acessar esse link.
Observação: Durante esse documento todas as descobertas feitas estarão grifadas em negrito.
No experimento de coleta desses dados, usuários foram divididos em 3 grupos e avaliaram 5 formas de organizar uma playlist. A avaliação é uma nota de 1 à 5, onde quanto maior a nota, mais o usuário está satisfeito com aquela maneira de organizar a playlist.
Rows: 115
Columns: 4
$ user_id <chr> "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11",…
$ satisfaction <dbl> 2.0, 3.0, 1.5, 1.0, 2.0, 2.5, 2.0, 3.0, 2.0, 2.0, 2.0, 2…
$ scenario <chr> "baseline", "baseline", "baseline", "baseline", "baselin…
$ group <chr> "3", "1", "2", "2", "1", "1", "3", "3", "2", "1", "3", "…
O que cada coluna significa:
user_id: O id do usuário que fez a avaliação.satisfaction: A avaliação do usuário, ou seja, o quão satisfeito ele está com o método que está sendo avaliado.scenario: O método que está sendo avaliado.group: O grupo que o usuário fez parte durante a avaliação.Como os métodos estão divididos:
baseline: Uma fila simples. Quando uma nova música é adicionada à playlist, ela é apenas posta no final da fila.combined: Todos os métodos são colocados juntos.like/dislike: Apresenta um recurso visual que indica aos usuários se os outros usuários estão gostando (Like) ou não (Dislike) das músicas que estão sendo adicionadas.skip: Permite que os usuários decidam se pulam uma música da playlist. Se determinada quantidade de usuários votarem que querem pular essa música, a música que está sendo tocada é pausada e a proxima da fila é reproduzida.up/downvoting: De acordo com as avaliações dos usuários, as músicas mais bem avaliadas são tocadas primeiro e as piores avaliadas são tocadas depois. Esse metodo é similar ao usado pelo Reddit. O usuário pode escolher entre avaliar bem (Upvoting) e avaliar mal (Downvoting) uma música. O que define a avaliação geral da música é o somatorio de todas as avaliações dos usuários que estão escutando a playlist.O que interessa principalmente na análise que vamos fazer é chegar em qual o melhor método para a montagem de uma playlist colaborativa. Para isso iremos analisar em como a avaliação do usuário (satisfaction) está relacionada com o método usado (scenario).
Importante, é sabido que os grupos não afetam a metrica de satisfação, por isso, vamos ignorar esse dado nessa análise. Em casos que não se tem essa informação, deve-se levar em consideração que algum vies pode ter ocorrido por estar em um certo grupo.
Além disso, todos os métodos possuem a mesma quantidade de avaliações:
satisfacoes %>%
group_by(scenario) %>%
count() %>%
rename(
"Método" = scenario,
"Quantidade de avaliações" = n
)Para se ter uma primeira ideia de como esses dois dados estão relacionados, podemos fazer um plot de pontos para visualizarmos qual o formato da distribuição das avaliações (satisfaction) nos métodos usados (scenario):
satisfacoes %>%
ggplot(aes(
x = scenario,
y = satisfaction,
color = scenario
)) +
geom_quasirandom() +
geom_violin(fill = NA) +
scale_color_discrete(name = "Métodos") +
labs(
title = "Distribuição de pontos das avaliações de acordo com o método usado",
x = "Método usado",
y = "Avaliação do usuário"
)Nessa distribuição, podemos ver que existem alguns métodos que se destacam como os melhores, como o up/downvoting que obteve as melhores notas nesse experimento. Temos também métodos que se destacam de forma negativa, como por exemplo o baseline, sendo o único método que recebeu notas 1.
Além disso, podemos dividir visualmente os métodos em dois grupos:
like/dislike, combined e up/downvotingbaseline e skipPara termos uma ideia melhor da avaliação desses métodos de forma geral, podemos usar uma médida de centro, como a média, para comprimir esses dados:
satisfacoes %>%
group_by(scenario) %>%
summarise(media = mean(satisfaction)) %>%
ggplot(aes(
x = reorder(scenario, media),
y = media,
color = reorder(scenario, media)
)) +
geom_point(
size = 5,
alpha = 0.75
) +
ylim(1,5) +
scale_color_discrete(name = "Métodos") +
labs(
title = "Distribuição das médias das avaliações de acordo com o método usado",
x = "Método usado",
y = "Média das avaliações dos usuário"
) Como podemos ver, a partir dessas médias, podemos estabelecer uma hierarquia de preferencia para a maioria do usuários do experimento quanto ao método usado:
up/downvotingcombinedlike/dislikeskipbaselineEssa hierarquia vale apenas para a amostra de dados que possuimos, ainda não temos nenhuma informação quanto a população
Para aprofundarmos a analise, precisamos fazer uma generalização, ou seja, precisamos saber se realmente há uma preferencia quanto ao método usado, tendo em vista a população.
Para isso, podemos calcular alguns intervalos de confiança com bootstraping para as médias e ver se o comportamento da amostra se perpetua para a população.
Intervalos de confiança são estimativas para uma certa médida desconhecida da população, ou seja, é um intervalo que, a partir de um nível de confiança, determina quais os valores que uma médida pode assumir. O nível de confiança é o nível de certeza que o valor daquela médida na população, estará no intervalo.
Bootstraping é o método de criar novas amostras que representam bem a população a partir de reamostragem com reposição. A amostra usada para realização desse método é uma amostra que representa a população bem.
O nome desse processo é inferência estatística. Análisar a amostra e a partir dela, deduzir com um grau de confiança informações sobre a população.
Primeiro temos que definir, qual o nivel de confiança desejado e quantas amostras queremos usar em cada boostraping:
[1] "Nivel de confiança: 0.95"
[1] "Quantidade de amostras: 4000"
Também precisamos definir como calcularemos a métrica que será medida a cada bootstraping. Para isso, definimos uma função que recebe o dataset (d), os indices das linhas que foram escolhidas para a nova amostra (i) e qual cálculo será feito:
calcula_media = function(d, i) {
d %>%
slice(i) %>%
summarise(media = mean(satisfaction)) %>%
pull(media)
}Por fim, podemos definir uma função que calcula o intervalo de confiança da média da avaliação de um método na população. Fazemos isso com um bootstrap a partir do nome daquele método (scenario_bootstrap).
Observação: Estamos utilizando a biblioteca boot para gerar o bootstrap e a biblioteca broom para facilitar a integração com outras bibliotecas do conjunto tidyverse.
bootstrap_satisfacao = function(scenario_bootstrap) {
satisfacoes_scenario = satisfacoes %>%
filter(scenario == scenario_bootstrap)
satisfacoes_scenario %>%
boot(statistic = calcula_media, R = quantidade_amostras) %>%
tidy(conf.level = nivel_confianca,
conf.int = TRUE)
}A partir dessa função podemos calcular o intervalo de confiança das médias de todos os métodos na população.
Primeiro, identificamos quais o métodos foram usados:
tipos_scenario = satisfacoes %>%
select(scenario) %>%
unique()
tipos_scenario %>%
rename(
"Métodos" = scenario
)E depois geramos as médias para cada um desses métodos:
ics_scenarios = tipos_scenario %>%
cbind(map_df(tipos_scenario$scenario, ~ bootstrap_satisfacao(.))) %>%
select(-bias, -std.error)
ics_scenarios %>%
rename(
'Método' = scenario,
'Média' = statistic,
'Limite inferior' = conf.low,
'Limite superior' = conf.high,
)Além das médias, podemos ver que obtemos o Limite inferior e o Limite superior. Esses limites são o que formam o intervalo de confiança.
Para visualizarmos como a média se comporta na população, podemos plotar num grafico, similar ao que fizemos para a média da amostra, a média da população resultante do bootstrap. Além da média, iremos plotar o intervalo de confiança, já que, a média real das avaliações da população está dentro desse intervalo em qualquer lugar com 95% de confiança.
ics_scenarios %>%
ggplot(aes(
x = reorder(scenario, statistic),
y = statistic,
ymin = conf.low,
ymax = conf.high,
color = reorder(scenario, statistic)
)) +
geom_point(size = 4) +
geom_errorbar() +
ylim(1,5) +
scale_color_discrete(name = "Métodos") +
labs(
title = "Intervalos de confiança a partir da avaliação de um método",
x = "Método",
y = "Avaliação"
) A partir dessa visualização podemos fazer algumas inferências sobre a população com um nível de confiança de 95%:
baseline ou o skip.up/downvoting ou o combined.up/downvoting, combined e like/dislike foram melhores avaliados em relação aos métodos baseline e skip.Também existem inferências que não podemos fazer:
up/downvoting: Não podemos afirmar isso pois existe uma interseção entre o intervalo de confiança desse método e do método combined, ou seja, existe a possibilidade de combined ser melhor avaliado do que o up/downvoting.baseline: Não podemos afirmar isso pelo menos motivo já apresentado para a afirmação de qual método foi melhor avaliado. O intervalo de confiança do método baseline possui uma interseção com o intervalo de confiança do método skip.Não conseguimos descobrir qual o método mais bem avaliado pelos usuários na população usando a média. No entanto, podemos fazer um bootstrap comparando a diferença das médias entre dois métodos.
Para isso, precisamos de todas as combinações de como podemos comparar os métodos, ou seja, todos os pares de métodos possiveis:
Observação: No código abaixo foi utilizado permutações para no futuro visualizar essas diferenças num gráfico.
combinacoes_scenario = tipos_scenario %>%
pull(scenario) %>%
gtools::permutations(n = 5, r = 2)
combinacoes_scenario [,1] [,2]
[1,] "baseline" "combined"
[2,] "baseline" "like/dislike"
[3,] "baseline" "skip"
[4,] "baseline" "up/downvoting"
[5,] "combined" "baseline"
[6,] "combined" "like/dislike"
[7,] "combined" "skip"
[8,] "combined" "up/downvoting"
[9,] "like/dislike" "baseline"
[10,] "like/dislike" "combined"
[11,] "like/dislike" "skip"
[12,] "like/dislike" "up/downvoting"
[13,] "skip" "baseline"
[14,] "skip" "combined"
[15,] "skip" "like/dislike"
[16,] "skip" "up/downvoting"
[17,] "up/downvoting" "baseline"
[18,] "up/downvoting" "combined"
[19,] "up/downvoting" "like/dislike"
[20,] "up/downvoting" "skip"
Além disso, precisamos de uma função que gera o bootstrap a partir de dois nomes de métodos:
bootstrap_diferenca = function(scenario1, scenario2) {
calcula_diferenca = function(d, i) {
medias = d %>%
slice(i) %>%
group_by(scenario) %>%
summarise(media = mean(satisfaction))
scenario1_media = medias %>% filter(scenario == scenario1) %>% pull(media)
scenario2_media = medias %>% filter(scenario == scenario2) %>% pull(media)
scenario2_media - scenario1_media
}
satisfacoes_scenario = satisfacoes %>%
filter(scenario %in% c(scenario1, scenario2))
satisfacoes_scenario %>%
boot(statistic = calcula_diferenca, R = quantidade_amostras) %>%
tidy(conf.level = nivel_confianca,
conf.int = TRUE)
}Agora, podemos gerar as diferenças a partir das combinações e da função que faz o bootstrap. Passamos através de um map todas as combinações possiveis e como função para ser executada pelo map, uma função que é responsavel por criar a linha da tabela com a identificação dos métodos comparados e o boostrap referente aquela comparação:
build_line = function(comb_number) {
scenario1 = combinacoes_scenario[comb_number, 1]
scenario2 = combinacoes_scenario[comb_number, 2]
cbind(
tibble(scenario1, scenario2),
bootstrap_diferenca(scenario1, scenario2)
)
}
diferencas = map_df(1:NROW(combinacoes_scenario), ~ build_line(.))
diferencas %>%
select(-bias, -std.error) %>%
rename(
"Método 1" = scenario1,
"Método 2" = scenario2,
"Média" = statistic,
"Limite inferior" = conf.low,
"Limite superior" = conf.high
)Uma pergunta relevante é se existe alguma interseção dos intervalos de confiança com o 0, ou seja, se existe algum caso em que não se pode identificar a preferência de um método em relação a outro. Para responder essa pergunta, podemos ver se os sinais do limite superior e do limite superior são diferentes, se forem, isso quer dizer que o intervalo de confiança contem o 0 e com isso não podemos identificar a preferencia:
n_intersecoes = diferencas %>%
filter(sign(conf.high) != sign(conf.low)) %>%
NROW()
paste("Número de interseções: ", n_intersecoes)[1] "Número de interseções: 0"
Nesse caso, não vemos nenhuma interseção e podemos inferir a preferencia de um método em relação a outro em todos os casos.
Podemos também visualizar essas diferenças em um gráfico:
diferencas %>%
ggplot(aes(
x = scenario2,
y = statistic,
ymin = conf.low,
ymax = conf.high,
color = scenario2
)) +
geom_pointrange() +
geom_hline(yintercept = 0, linetype = "dashed", alpha = 0.5) +
coord_flip() +
ylim(-3, 3) +
facet_wrap(~ scenario1) +
labs(
x = '',
y = ''
)Por fim, podemos fazer um ranking de métodos tendo em vista a quantidade de métodos que ele teve uma avaliação melhor.
Fazemos isso comparando o sinal de um dos limites do intervalo de confiança - Não importa qual limite pois os dois limites de todas as comparações tem sinal igual - e vendo se aquele sinal é negativo. Se for negativo, quer dizer que o método base é mais eficiente que o método que está se comparando. Se não for, quer dizer que o método comparado é mais eficiente.
Com isso, fazemos a comparação entre todos os métodos, fazemos a contagem de quantos métodos aquele se sobressai e criamos o ranking:
calcula_sobressai = function(diferencas, scenario) {
diferencas %>%
filter(scenario1 == scenario) %>%
filter(sign(conf.low) == -1) %>%
NROW()
}
tipos_scenario %>%
mutate(sobressai = map_dbl(scenario, ~ calcula_sobressai(diferencas, .))) %>%
mutate(posicao = 5 - sobressai) %>%
arrange(posicao) %>%
rename(
"Método" = scenario,
"Posição" = posicao,
"Se sobressai em X casos" = sobressai
)Como podemos ver, conseguimos inferir para a população qual método seria o mais bem avaliado. Nesse caso, o mais bem avaliado seria o up/downvoting.