Practice 2:

Bài thực hành mô hình hóa bằng thang đo Likert (Likert Scales hay Likert Data)

Thang đo Likert là thang đánh giá thường được sử dụng trong các surveys. Thang đo Likert thường có 5 cấp độ điển hình: 1. Rất không đồng ý 2. Không đồng ý 3. Trung lập 4. Đồng ý 6. Hoàn toàn đồng ý

Một số graph của thang đo Likert: 1. 100% staked bar 2. Diverging bars with neutrals separate 3. Diverging bars with neutrals split 4. Small multiple bars

Ở bài viết này, tôi thực hành minh họa kiểu 1 - 100% staked bar:

# Reference: http://daydreamingnumbers.com/blog/4-ways-to-visualize-likert-scales/
# Reference: https://rpubs.com/chidungkt/637110

library(tidyverse)
library(extrafont)

size <- 100000

# 5 level likert

responses <- c("Like them a lot", "Like them", "Neutrals", "Dislike them", "Dislike a lot")

brand <- c(rep("Bounty", size), 
           rep("Snickers", size), 
           rep("Milky Way", size), 
           rep("Mars", size), 
           rep("Galaxy Caramel", size), 
           rep("Twix", size), 
           rep("Galaxy", size), 
           rep("Teaser", size))

lik_res <- c(sample(responses, size = size, replace = TRUE, prob = c(24, 36, 15, 11, 14)), 
             sample(responses, size = size, replace = TRUE, prob = c(23, 42, 17, 9, 9)), 
             sample(responses, size = size, replace = TRUE, prob = c(16, 52, 23, 6, 3)), 
             sample(responses, size = size, replace = TRUE, prob = c(15, 57, 18, 7, 3)), 
             sample(responses, size = size, replace = TRUE, prob = c(27, 46, 17, 8, 2)), 
             sample(responses, size = size, replace = TRUE, prob = c(18, 57, 19, 5, 1)), 
             sample(responses, size = size, replace = TRUE, prob = c(29, 51, 15, 4, 1)), 
             sample(responses, size = size, replace = TRUE, prob = c(39, 42, 14, 4, 1)))


df_data <- tibble(brand = brand, lik_res = lik_res)

# Prepare data: 

df_data %>% 
  group_by(brand, lik_res) %>% 
  count() %>% 
  ungroup() %>% 
  group_by(brand) %>% 
  mutate(percent = 100*n / sum(n)) %>% 
  mutate(percent = round(percent, 0)) %>% 
  mutate(bar_text = paste0(percent, "%")) %>% 
  ungroup() -> df_ploting

df_ploting %>% 
  filter(lik_res == responses[5]) %>% 
  arrange(percent) %>% 
  pull(brand) -> order_x

# ploting: 

my_colors <- c("#3e6487", "#829cb2", "#c7cdd1", "#edad88", "#e36c33")
my_font <- "Roboto Condensed"

theme_set(theme_minimal())


df_ploting %>% 
  mutate(brand = factor(brand, levels = order_x), lik_res = factor(lik_res, levels = responses[5:1])) -> odered


ploting <- odered %>% 
  ggplot(aes(x = brand, y = percent, fill = lik_res)) + 
  geom_col(width = 0.8) + 
  coord_flip() + 
  scale_fill_manual(values = my_colors[5:1], name = "") + 
  theme(legend.position = "top") + 
  theme(text = element_text(family = my_font)) + 
  guides(fill = guide_legend(reverse = TRUE)) + 
  scale_y_continuous(labels = paste0(seq(0, 100, 25), "%"), expand = c(0, 0)) + 
  theme(plot.title = element_text(size = 20), plot.subtitle = element_text(size = 12, color = "grey20")) + 
  theme(plot.caption = element_text(family = my_font, size = 12, colour = "grey20", face = "italic")) +
  theme(axis.text = element_text(color = "grey20", size = 10.2)) + 
  theme(plot.margin = unit(rep(0.7, 4), "cm")) + 
  theme(panel.grid.major.y = element_blank(), panel.grid.minor.x = element_blank()) + 
  theme(legend.key.height = unit(0.15, "mm")) + 
  labs(x = NULL, y = NULL, 
       title = "Everyone likes chocolates, but Bounty and Snickers get the\nmost extreme opinions", 
       subtitle = "Likert scale is a type of rating scale commonly used in surveys. When responding to a Likert type question,\nrespondents simply state their level of agreement or disagreement on a symmetric agree-disagree scale.",
       caption = "Source: http://daydreamingnumbers.com/blog/")


# For displaying percent of "Dislike a lot": 

odered %>% 
  filter(lik_res == "Dislike a lot") %>% 
  filter(percent >= 3) -> df_text1

# For displaying percent of "Like them a lot": 

odered %>% 
  filter(lik_res == "Like them a lot") -> df_text2

# ploting fn: 

ploting + 
  geom_text(data = df_text1 %>% filter(brand != "Bounty"), aes(x = brand, y = 100 - 1.6, label = bar_text), size = 4, color = "white", family = my_font) + 
  geom_text(data = df_text1 %>% filter(brand == "Bounty"), aes(x = brand, y = 100 - 2.3, label = bar_text), size = 4, color = "white", family = my_font) + 
  geom_text(data = df_text2, aes(x = brand, y = 2.3, label = bar_text), size = 4, color = "white", family = my_font)

LS0tDQp0aXRsZTogJ1ByYWN0aWNlIDI6IFZpc3VhbGl6ZSBMaWtlcnQgU2NhbGVzJw0KYXV0aG9yOiAiTmd1eWVuIFRoaSBOZ29jIEh1eWVuIg0KZGF0ZTogIjMvNC8yMDIxIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgaGlnaGxpZ2h0OiB6ZW5idXJuDQogICAgIyBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRoZW1lOiBmbGF0bHkNCiAgICB0b2M6IFRSVUUNCiAgICB0b2NfZmxvYXQ6IFRSVUUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNikNCmBgYA0KDQojIFByYWN0aWNlIDI6DQpCw6BpIHRo4buxYyBow6BuaCBtw7QgaMOsbmggaMOzYSBi4bqxbmcgdGhhbmcgxJFvIExpa2VydCAoTGlrZXJ0IFNjYWxlcyBoYXkgTGlrZXJ0IERhdGEpDQoNClRoYW5nIMSRbyBMaWtlcnQgbMOgIHRoYW5nIMSRw6FuaCBnacOhIHRoxrDhu51uZyDEkcaw4bujYyBz4butIGThu6VuZyB0cm9uZyBjw6FjIHN1cnZleXMuIFRoYW5nIMSRbyBMaWtlcnQgdGjGsOG7nW5nIGPDsyA1IGPhuqVwIMSR4buZIMSRaeG7g24gaMOsbmg6DQoxLiBS4bqldCBraMO0bmcgxJHhu5NuZyDDvQ0KMi4gS2jDtG5nIMSR4buTbmcgw70NCjMuIFRydW5nIGzhuq1wDQo0LiDEkOG7k25nIMO9DQo2LiBIb8OgbiB0b8OgbiDEkeG7k25nIMO9DQoNCk3hu5l0IHPhu5EgZ3JhcGggY+G7p2EgdGhhbmcgxJFvIExpa2VydDoNCjEuIDEwMCUgc3Rha2VkIGJhcg0KMi4gRGl2ZXJnaW5nIGJhcnMgd2l0aCBuZXV0cmFscyBzZXBhcmF0ZQ0KMy4gRGl2ZXJnaW5nIGJhcnMgd2l0aCBuZXV0cmFscyBzcGxpdA0KNC4gU21hbGwgbXVsdGlwbGUgYmFycw0KDQrhu54gYsOgaSB2aeG6v3QgbsOgeSwgdMO0aSB0aOG7sWMgaMOgbmggbWluaCBo4buNYSBraeG7g3UgMSAtIDEwMCUgc3Rha2VkIGJhcjoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KIyBSZWZlcmVuY2U6IGh0dHA6Ly9kYXlkcmVhbWluZ251bWJlcnMuY29tL2Jsb2cvNC13YXlzLXRvLXZpc3VhbGl6ZS1saWtlcnQtc2NhbGVzLw0KIyBSZWZlcmVuY2U6IGh0dHBzOi8vcnB1YnMuY29tL2NoaWR1bmdrdC82MzcxMTANCg0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGV4dHJhZm9udCkNCg0Kc2l6ZSA8LSAxMDAwMDANCg0KIyA1IGxldmVsIGxpa2VydA0KDQpyZXNwb25zZXMgPC0gYygiTGlrZSB0aGVtIGEgbG90IiwgIkxpa2UgdGhlbSIsICJOZXV0cmFscyIsICJEaXNsaWtlIHRoZW0iLCAiRGlzbGlrZSBhIGxvdCIpDQoNCmJyYW5kIDwtIGMocmVwKCJCb3VudHkiLCBzaXplKSwgDQogICAgICAgICAgIHJlcCgiU25pY2tlcnMiLCBzaXplKSwgDQogICAgICAgICAgIHJlcCgiTWlsa3kgV2F5Iiwgc2l6ZSksIA0KICAgICAgICAgICByZXAoIk1hcnMiLCBzaXplKSwgDQogICAgICAgICAgIHJlcCgiR2FsYXh5IENhcmFtZWwiLCBzaXplKSwgDQogICAgICAgICAgIHJlcCgiVHdpeCIsIHNpemUpLCANCiAgICAgICAgICAgcmVwKCJHYWxheHkiLCBzaXplKSwgDQogICAgICAgICAgIHJlcCgiVGVhc2VyIiwgc2l6ZSkpDQoNCmxpa19yZXMgPC0gYyhzYW1wbGUocmVzcG9uc2VzLCBzaXplID0gc2l6ZSwgcmVwbGFjZSA9IFRSVUUsIHByb2IgPSBjKDI0LCAzNiwgMTUsIDExLCAxNCkpLCANCiAgICAgICAgICAgICBzYW1wbGUocmVzcG9uc2VzLCBzaXplID0gc2l6ZSwgcmVwbGFjZSA9IFRSVUUsIHByb2IgPSBjKDIzLCA0MiwgMTcsIDksIDkpKSwgDQogICAgICAgICAgICAgc2FtcGxlKHJlc3BvbnNlcywgc2l6ZSA9IHNpemUsIHJlcGxhY2UgPSBUUlVFLCBwcm9iID0gYygxNiwgNTIsIDIzLCA2LCAzKSksIA0KICAgICAgICAgICAgIHNhbXBsZShyZXNwb25zZXMsIHNpemUgPSBzaXplLCByZXBsYWNlID0gVFJVRSwgcHJvYiA9IGMoMTUsIDU3LCAxOCwgNywgMykpLCANCiAgICAgICAgICAgICBzYW1wbGUocmVzcG9uc2VzLCBzaXplID0gc2l6ZSwgcmVwbGFjZSA9IFRSVUUsIHByb2IgPSBjKDI3LCA0NiwgMTcsIDgsIDIpKSwgDQogICAgICAgICAgICAgc2FtcGxlKHJlc3BvbnNlcywgc2l6ZSA9IHNpemUsIHJlcGxhY2UgPSBUUlVFLCBwcm9iID0gYygxOCwgNTcsIDE5LCA1LCAxKSksIA0KICAgICAgICAgICAgIHNhbXBsZShyZXNwb25zZXMsIHNpemUgPSBzaXplLCByZXBsYWNlID0gVFJVRSwgcHJvYiA9IGMoMjksIDUxLCAxNSwgNCwgMSkpLCANCiAgICAgICAgICAgICBzYW1wbGUocmVzcG9uc2VzLCBzaXplID0gc2l6ZSwgcmVwbGFjZSA9IFRSVUUsIHByb2IgPSBjKDM5LCA0MiwgMTQsIDQsIDEpKSkNCg0KDQpkZl9kYXRhIDwtIHRpYmJsZShicmFuZCA9IGJyYW5kLCBsaWtfcmVzID0gbGlrX3JlcykNCg0KIyBQcmVwYXJlIGRhdGE6IA0KDQpkZl9kYXRhICU+JSANCiAgZ3JvdXBfYnkoYnJhbmQsIGxpa19yZXMpICU+JSANCiAgY291bnQoKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIGdyb3VwX2J5KGJyYW5kKSAlPiUgDQogIG11dGF0ZShwZXJjZW50ID0gMTAwKm4gLyBzdW0obikpICU+JSANCiAgbXV0YXRlKHBlcmNlbnQgPSByb3VuZChwZXJjZW50LCAwKSkgJT4lIA0KICBtdXRhdGUoYmFyX3RleHQgPSBwYXN0ZTAocGVyY2VudCwgIiUiKSkgJT4lIA0KICB1bmdyb3VwKCkgLT4gZGZfcGxvdGluZw0KDQpkZl9wbG90aW5nICU+JSANCiAgZmlsdGVyKGxpa19yZXMgPT0gcmVzcG9uc2VzWzVdKSAlPiUgDQogIGFycmFuZ2UocGVyY2VudCkgJT4lIA0KICBwdWxsKGJyYW5kKSAtPiBvcmRlcl94DQoNCiMgcGxvdGluZzogDQoNCm15X2NvbG9ycyA8LSBjKCIjM2U2NDg3IiwgIiM4MjljYjIiLCAiI2M3Y2RkMSIsICIjZWRhZDg4IiwgIiNlMzZjMzMiKQ0KbXlfZm9udCA8LSAiUm9ib3RvIENvbmRlbnNlZCINCg0KdGhlbWVfc2V0KHRoZW1lX21pbmltYWwoKSkNCg0KDQpkZl9wbG90aW5nICU+JSANCiAgbXV0YXRlKGJyYW5kID0gZmFjdG9yKGJyYW5kLCBsZXZlbHMgPSBvcmRlcl94KSwgbGlrX3JlcyA9IGZhY3RvcihsaWtfcmVzLCBsZXZlbHMgPSByZXNwb25zZXNbNToxXSkpIC0+IG9kZXJlZA0KDQoNCnBsb3RpbmcgPC0gb2RlcmVkICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gYnJhbmQsIHkgPSBwZXJjZW50LCBmaWxsID0gbGlrX3JlcykpICsgDQogIGdlb21fY29sKHdpZHRoID0gMC44KSArIA0KICBjb29yZF9mbGlwKCkgKyANCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gbXlfY29sb3JzWzU6MV0sIG5hbWUgPSAiIikgKyANCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpICsgDQogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gbXlfZm9udCkpICsgDQogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHJldmVyc2UgPSBUUlVFKSkgKyANCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHBhc3RlMChzZXEoMCwgMTAwLCAyNSksICIlIiksIGV4cGFuZCA9IGMoMCwgMCkpICsgDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSwgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGNvbG9yID0gImdyZXkyMCIpKSArIA0KICB0aGVtZShwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gbXlfZm9udCwgc2l6ZSA9IDEyLCBjb2xvdXIgPSAiZ3JleTIwIiwgZmFjZSA9ICJpdGFsaWMiKSkgKw0KICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiZ3JleTIwIiwgc2l6ZSA9IDEwLjIpKSArIA0KICB0aGVtZShwbG90Lm1hcmdpbiA9IHVuaXQocmVwKDAuNywgNCksICJjbSIpKSArIA0KICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksIHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSkgKyANCiAgdGhlbWUobGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KDAuMTUsICJtbSIpKSArIA0KICBsYWJzKHggPSBOVUxMLCB5ID0gTlVMTCwgDQogICAgICAgdGl0bGUgPSAiRXZlcnlvbmUgbGlrZXMgY2hvY29sYXRlcywgYnV0IEJvdW50eSBhbmQgU25pY2tlcnMgZ2V0IHRoZVxubW9zdCBleHRyZW1lIG9waW5pb25zIiwgDQogICAgICAgc3VidGl0bGUgPSAiTGlrZXJ0IHNjYWxlIGlzIGEgdHlwZSBvZiByYXRpbmcgc2NhbGUgY29tbW9ubHkgdXNlZCBpbiBzdXJ2ZXlzLiBXaGVuIHJlc3BvbmRpbmcgdG8gYSBMaWtlcnQgdHlwZSBxdWVzdGlvbixcbnJlc3BvbmRlbnRzIHNpbXBseSBzdGF0ZSB0aGVpciBsZXZlbCBvZiBhZ3JlZW1lbnQgb3IgZGlzYWdyZWVtZW50IG9uIGEgc3ltbWV0cmljIGFncmVlLWRpc2FncmVlIHNjYWxlLiIsDQogICAgICAgY2FwdGlvbiA9ICJTb3VyY2U6IGh0dHA6Ly9kYXlkcmVhbWluZ251bWJlcnMuY29tL2Jsb2cvIikNCg0KDQojIEZvciBkaXNwbGF5aW5nIHBlcmNlbnQgb2YgIkRpc2xpa2UgYSBsb3QiOiANCg0Kb2RlcmVkICU+JSANCiAgZmlsdGVyKGxpa19yZXMgPT0gIkRpc2xpa2UgYSBsb3QiKSAlPiUgDQogIGZpbHRlcihwZXJjZW50ID49IDMpIC0+IGRmX3RleHQxDQoNCiMgRm9yIGRpc3BsYXlpbmcgcGVyY2VudCBvZiAiTGlrZSB0aGVtIGEgbG90IjogDQoNCm9kZXJlZCAlPiUgDQogIGZpbHRlcihsaWtfcmVzID09ICJMaWtlIHRoZW0gYSBsb3QiKSAtPiBkZl90ZXh0Mg0KDQojIHBsb3RpbmcgZm46IA0KDQpwbG90aW5nICsgDQogIGdlb21fdGV4dChkYXRhID0gZGZfdGV4dDEgJT4lIGZpbHRlcihicmFuZCAhPSAiQm91bnR5IiksIGFlcyh4ID0gYnJhbmQsIHkgPSAxMDAgLSAxLjYsIGxhYmVsID0gYmFyX3RleHQpLCBzaXplID0gNCwgY29sb3IgPSAid2hpdGUiLCBmYW1pbHkgPSBteV9mb250KSArIA0KICBnZW9tX3RleHQoZGF0YSA9IGRmX3RleHQxICU+JSBmaWx0ZXIoYnJhbmQgPT0gIkJvdW50eSIpLCBhZXMoeCA9IGJyYW5kLCB5ID0gMTAwIC0gMi4zLCBsYWJlbCA9IGJhcl90ZXh0KSwgc2l6ZSA9IDQsIGNvbG9yID0gIndoaXRlIiwgZmFtaWx5ID0gbXlfZm9udCkgKyANCiAgZ2VvbV90ZXh0KGRhdGEgPSBkZl90ZXh0MiwgYWVzKHggPSBicmFuZCwgeSA9IDIuMywgbGFiZWwgPSBiYXJfdGV4dCksIHNpemUgPSA0LCBjb2xvciA9ICJ3aGl0ZSIsIGZhbWlseSA9IG15X2ZvbnQpDQoNCmBgYA0KDQohW10oRDpcUlxwcmFjdC5wbmcpDQoNCg==