Load the data

data <- read.csv('politics_approval_rates.csv')
df <- data.frame(data)
df

Our goal is to visualize the data with a stacked bar chart. But if you try to make a bar plot, one problem arises: Which value are you going to use as the height? Is it the Approve?

barplot(names.arg=df$Issue, height=df$Approve, col = '#ffddcc')

But that’s not what we want. We want to use all the values to compare them in the same bar.

Anatomy of a stacked bar chart

knitr::include_graphics("stacked_bar.png")

3 elements are important: * Category: the categories are displayed on the x axis (ex: profession) * Subcategory: Each main category contains subcategories stacked on top of each other (ex: gender) * Height: the height of the bar represents the values are displayed on y axis (ex: salary).

Now, we need to find our category, subcategory and opinion:

  1. Category: the issue (Race, Education, etc.)
  2. Subcategory: the opinion. Either approve, disapprove or no opinion)
  3. Height: the value for each opinion

That’s done. The next step is to trasnform the data.

transform the data

The final data will looks like this:

knitr::include_graphics("final_df.png")

Each Category will have: * 3 subcategories * 3 heights respective to the subcategories

The category - issues

We have 13 issues, we want to replicated them 3 times because we have 3 subcategories.

# loop through each of the 13 issues
issues <- c()
for (issue in df$Issue)
  # replicate the issue 3 times 
  issues <- c(issues, rep(issue, 3))

# 13 * 3 = 39 issues
issues
 [1] "Race relations"           "Race relations"           "Race relations"          
 [4] "Education"                "Education"                "Education"               
 [7] "Terrorism"                "Terrorism"                "Terrorism"               
[10] "Energy policy"            "Energy policy"            "Energy policy"           
[13] "Foreign affairs"          "Foreign affairs"          "Foreign affairs"         
[16] "Environment"              "Environment"              "Environment"             
[19] "Situation in Iraq"        "Situation in Iraq"        "Situation in Iraq"       
[22] "Taxes"                    "Taxes"                    "Taxes"                   
[25] "Healthcare policy"        "Healthcare policy"        "Healthcare policy"       
[28] "Economy"                  "Economy"                  "Economy"                 
[31] "Situation in Afghanistan" "Situation in Afghanistan" "Situation in Afghanistan"
[34] "Federal budget deficit"   "Federal budget deficit"   "Federal budget deficit"  
[37] "Immigration"              "Immigration"              "Immigration"             

The subcategories - opinions

We have 3 opinions: approve, disapprove and no opinion.

opinions <- colnames(df[, 2:4])
opinions
[1] "Approve"    "Disapprove" "No.Opinion"

Now we replicate the subcategories. Look at opinions from the final data frame from the picture above. This time we don’t need to loop through each opinion. Instead, we replicate all at once. We have 3 opinions, we replicate them 13 times to get the 39. (3x13 = 39)

# n_replications <- length(issues) / 3          # 39/3 = 13 replications
n_replications <- nrow(df)
opinions <- rep(opinions, n_replications)

opinions
 [1] "Approve"    "Disapprove" "No.Opinion" "Approve"    "Disapprove" "No.Opinion"
 [7] "Approve"    "Disapprove" "No.Opinion" "Approve"    "Disapprove" "No.Opinion"
[13] "Approve"    "Disapprove" "No.Opinion" "Approve"    "Disapprove" "No.Opinion"
[19] "Approve"    "Disapprove" "No.Opinion" "Approve"    "Disapprove" "No.Opinion"
[25] "Approve"    "Disapprove" "No.Opinion" "Approve"    "Disapprove" "No.Opinion"
[31] "Approve"    "Disapprove" "No.Opinion" "Approve"    "Disapprove" "No.Opinion"
[37] "Approve"    "Disapprove" "No.Opinion"

Height - the values

Now we just get the values from each row, then transform it into a 1D vector.

# get the columns that contains the opinion values
values <- df[2:4]
values

Transpose the values

values <- t(values) 
values
           [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13]
Approve      52   49   48   47   44   43   41   41   40    38    36    31    29
Disapprove   38   40   45   42   48   51   53   54   57    59    57    64    62
No.Opinion   10   11    7   11    8    6    6    5    3     3     7     5     9

Convert them to a vector

values <- as.vector(values)
values
 [1] 52 38 10 49 40 11 48 45  7 47 42 11 44 48  8 43 51  6 41 53  6 41 54  5 40 57  3 38 59
[30]  3 36 57  7 31 64  5 29 62  9

Now, we have the rows transposed and stacked of to of each other. Let’s make the combine all the columns (issues, opinions, values) into a data frame.

Loading the data into the final dataframe

final_df = data.frame(issues, opinions, values)
colnames(final_df)[3]  <- "Approval Rates"
head(final_df)
NA

Beautiful! Now here comes the fun part, visualization!

# load the ggplot2 library
library(ggplot2)

ggplot(final_df, aes(fill=opinions, y=`Approval Rates`, x=issues)) + 
  geom_bar(position="stack", stat="identity") + 
  theme(axis.text.x = element_text(angle = 45, margin = margin(t=30, "pt")))
Warning: NAs introduced by coercion


# load the ggplot2 library
library(ggplot2)

ggplot(final_df, aes(x=issues, y=`Approval Rates`, fill=opinions)) + 
  geom_col(position="dodge") + 
  theme(axis.text.x = element_text(angle = 45, margin = margin(t=30, "pt")))
Warning: NAs introduced by coercion

LS0tDQp0aXRsZTogIlN0YWNrZWQgQmFyIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyMgTG9hZCB0aGUgZGF0YQ0KDQpgYGB7cn0NCmRhdGEgPC0gcmVhZC5jc3YoJ3BvbGl0aWNzX2FwcHJvdmFsX3JhdGVzLmNzdicpDQpkZiA8LSBkYXRhLmZyYW1lKGRhdGEpDQpkZg0KYGBgDQoNCg0KT3VyIGdvYWwgaXMgdG8gdmlzdWFsaXplIHRoZSBkYXRhIHdpdGggYSBzdGFja2VkIGJhciBjaGFydC4gDQpCdXQgaWYgeW91IHRyeSB0byBtYWtlIGEgYmFyIHBsb3QsIG9uZSBwcm9ibGVtIGFyaXNlczogV2hpY2ggdmFsdWUgYXJlIHlvdSBnb2luZw0KdG8gdXNlIGFzIHRoZSBoZWlnaHQ/IElzIGl0IHRoZSBBcHByb3ZlPw0KDQpgYGB7cn0NCmJhcnBsb3QobmFtZXMuYXJnPWRmJElzc3VlLCBoZWlnaHQ9ZGYkQXBwcm92ZSwgY29sID0gJyNmZmRkY2MnKQ0KYGBgDQoNCkJ1dCB0aGF0J3Mgbm90IHdoYXQgd2Ugd2FudC4gV2Ugd2FudCB0byB1c2UgYWxsIHRoZSB2YWx1ZXMgdG8gY29tcGFyZSB0aGVtIGluIHRoZSBzYW1lIGJhci4NCg0KIyMgQW5hdG9teSBvZiBhIHN0YWNrZWQgYmFyIGNoYXJ0DQoNCmBgYHtyfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoInN0YWNrZWRfYmFyLnBuZyIpDQpgYGANCg0KMyBlbGVtZW50cyBhcmUgaW1wb3J0YW50Og0KKiBDYXRlZ29yeTogdGhlIGNhdGVnb3JpZXMgYXJlIGRpc3BsYXllZCBvbiB0aGUgeCBheGlzIChleDogcHJvZmVzc2lvbikNCiogU3ViY2F0ZWdvcnk6IEVhY2ggbWFpbiBjYXRlZ29yeSBjb250YWlucyBzdWJjYXRlZ29yaWVzIHN0YWNrZWQgb24gdG9wIG9mIGVhY2ggb3RoZXIgKGV4OiBnZW5kZXIpDQoqIEhlaWdodDogdGhlIGhlaWdodCBvZiB0aGUgYmFyIHJlcHJlc2VudHMgdGhlIHZhbHVlcyBhcmUgZGlzcGxheWVkIG9uIHkgYXhpcyAoZXg6IHNhbGFyeSkuIA0KDQoNCk5vdywgd2UgbmVlZCB0byBmaW5kIG91ciBjYXRlZ29yeSwgc3ViY2F0ZWdvcnkgYW5kIG9waW5pb246DQoNCjEuIENhdGVnb3J5OiB0aGUgaXNzdWUgKFJhY2UsIEVkdWNhdGlvbiwgZXRjLikNCjIuIFN1YmNhdGVnb3J5OiB0aGUgb3Bpbmlvbi4gRWl0aGVyIGFwcHJvdmUsIGRpc2FwcHJvdmUgb3Igbm8gb3BpbmlvbikNCjMuIEhlaWdodDogdGhlIHZhbHVlIGZvciBlYWNoIG9waW5pb24NCg0KVGhhdCdzIGRvbmUuIFRoZSBuZXh0IHN0ZXAgaXMgdG8gdHJhc25mb3JtIHRoZSBkYXRhLg0KDQojIyB0cmFuc2Zvcm0gdGhlIGRhdGENCg0KVGhlIGZpbmFsIGRhdGEgd2lsbCBsb29rcyBsaWtlIHRoaXM6DQoNCmBgYHtyfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImZpbmFsX2RmLnBuZyIpDQpgYGANCg0KRWFjaCBDYXRlZ29yeSB3aWxsIGhhdmU6DQoqIDMgc3ViY2F0ZWdvcmllcw0KKiAzIGhlaWdodHMgcmVzcGVjdGl2ZSB0byB0aGUgc3ViY2F0ZWdvcmllcw0KDQoNCiMjIyBUaGUgY2F0ZWdvcnkgLSBpc3N1ZXMNCldlIGhhdmUgMTMgaXNzdWVzLCB3ZSB3YW50IHRvIHJlcGxpY2F0ZWQgdGhlbSAzIHRpbWVzIGJlY2F1c2Ugd2UgaGF2ZSAzIHN1YmNhdGVnb3JpZXMuDQoNCg0KYGBge3J9DQojIGxvb3AgdGhyb3VnaCBlYWNoIG9mIHRoZSAxMyBpc3N1ZXMNCmlzc3VlcyA8LSBjKCkNCmZvciAoaXNzdWUgaW4gZGYkSXNzdWUpDQogICMgcmVwbGljYXRlIHRoZSBpc3N1ZSAzIHRpbWVzIA0KICBpc3N1ZXMgPC0gYyhpc3N1ZXMsIHJlcChpc3N1ZSwgMykpDQoNCiMgMTMgKiAzID0gMzkgaXNzdWVzDQppc3N1ZXMNCmBgYA0KDQoNCiMjIyBUaGUgc3ViY2F0ZWdvcmllcyAtIG9waW5pb25zDQpXZSBoYXZlIDMgb3BpbmlvbnM6IGFwcHJvdmUsIGRpc2FwcHJvdmUgYW5kIG5vIG9waW5pb24uDQoNCmBgYHtyfQ0Kb3BpbmlvbnMgPC0gY29sbmFtZXMoZGZbLCAyOjRdKQ0Kb3BpbmlvbnMNCmBgYA0KDQpOb3cgd2UgcmVwbGljYXRlIHRoZSBzdWJjYXRlZ29yaWVzLiBMb29rIGF0IG9waW5pb25zIGZyb20gdGhlIGZpbmFsIGRhdGEgZnJhbWUgZnJvbSB0aGUgcGljdHVyZSBhYm92ZS4NClRoaXMgdGltZSB3ZSBkb24ndCBuZWVkIHRvIGxvb3AgdGhyb3VnaCBlYWNoIG9waW5pb24uIA0KSW5zdGVhZCwgd2UgcmVwbGljYXRlICBhbGwgYXQgb25jZS4gV2UgaGF2ZSAzIG9waW5pb25zLCB3ZSByZXBsaWNhdGUgdGhlbSAxMyB0aW1lcyB0byBnZXQgdGhlIDM5LiAoM3gxMyA9IDM5KQ0KDQpgYGB7cn0NCiMgbl9yZXBsaWNhdGlvbnMgPC0gbGVuZ3RoKGlzc3VlcykgLyAzICAgICAgICAgICMgMzkvMyA9IDEzIHJlcGxpY2F0aW9ucw0Kbl9yZXBsaWNhdGlvbnMgPC0gbnJvdyhkZikNCm9waW5pb25zIDwtIHJlcChvcGluaW9ucywgbl9yZXBsaWNhdGlvbnMpDQoNCm9waW5pb25zDQpgYGANCg0KIyMjIEhlaWdodCAtIHRoZSB2YWx1ZXMNCk5vdyB3ZSBqdXN0IGdldCB0aGUgdmFsdWVzIGZyb20gZWFjaCByb3csIHRoZW4gdHJhbnNmb3JtIGl0IGludG8gYSAxRCB2ZWN0b3IuDQoNCmBgYHtyfQ0KIyBnZXQgdGhlIGNvbHVtbnMgdGhhdCBjb250YWlucyB0aGUgb3BpbmlvbiB2YWx1ZXMNCnZhbHVlcyA8LSBkZlsyOjRdDQp2YWx1ZXMNCmBgYA0KDQpUcmFuc3Bvc2UgdGhlIHZhbHVlcw0KYGBge3J9DQp2YWx1ZXMgPC0gdCh2YWx1ZXMpIA0KdmFsdWVzDQpgYGANCg0KDQpDb252ZXJ0IHRoZW0gdG8gYSB2ZWN0b3INCg0KYGBge3J9DQp2YWx1ZXMgPC0gYXMudmVjdG9yKHZhbHVlcykNCnZhbHVlcw0KYGBgDQpOb3csIHdlIGhhdmUgdGhlIHJvd3MgdHJhbnNwb3NlZCBhbmQgc3RhY2tlZCBvZiB0byBvZiBlYWNoIG90aGVyLg0KTGV0J3MgbWFrZSB0aGUgY29tYmluZSBhbGwgdGhlIGNvbHVtbnMgKGlzc3Vlcywgb3BpbmlvbnMsIHZhbHVlcykgaW50byBhIGRhdGEgZnJhbWUuDQoNCiMjIyBMb2FkaW5nIHRoZSBkYXRhIGludG8gdGhlIGZpbmFsIGRhdGFmcmFtZQ0KDQpgYGB7cn0NCmZpbmFsX2RmID0gZGF0YS5mcmFtZShpc3N1ZXMsIG9waW5pb25zLCB2YWx1ZXMpDQpjb2xuYW1lcyhmaW5hbF9kZilbM10gIDwtICJBcHByb3ZhbCBSYXRlcyINCmhlYWQoZmluYWxfZGYpDQoNCmBgYA0KDQpCZWF1dGlmdWwhIE5vdyBoZXJlIGNvbWVzIHRoZSBmdW4gcGFydCwgdmlzdWFsaXphdGlvbiENCg0KYGBge3J9DQojIGxvYWQgdGhlIGdncGxvdDIgbGlicmFyeQ0KbGlicmFyeShnZ3Bsb3QyKQ0KDQpnZ3Bsb3QoZmluYWxfZGYsIGFlcyhmaWxsPW9waW5pb25zLCB5PWBBcHByb3ZhbCBSYXRlc2AsIHg9aXNzdWVzKSkgKyANCiAgZ2VvbV9iYXIocG9zaXRpb249InN0YWNrIiwgc3RhdD0iaWRlbnRpdHkiKSArIA0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBtYXJnaW4gPSBtYXJnaW4odD0zMCwgInB0IikpKQ0KYGBgDQpgYGB7cn0NCg0KIyBsb2FkIHRoZSBnZ3Bsb3QyIGxpYnJhcnkNCmxpYnJhcnkoZ2dwbG90MikNCg0KZ2dwbG90KGZpbmFsX2RmLCBhZXMoeD1pc3N1ZXMsIHk9YEFwcHJvdmFsIFJhdGVzYCwgZmlsbD1vcGluaW9ucykpICsgDQogIGdlb21fY29sKHBvc2l0aW9uPSJkb2RnZSIpICsgDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIG1hcmdpbiA9IG1hcmdpbih0PTMwLCAicHQiKSkpDQpgYGANCg0K