Chapter 40: R Bar Charts
R Bar Charts.
Bar charts are probably the single most used and most useful chart type in the entire world of data visualization — especially in business, reports, research papers, dashboards, and presentations. They are simple, easy to read, very accurate for comparison, and almost never misleading when made correctly (unlike pie charts, which we discussed earlier).
Today I’ll explain bar charts in R very thoroughly — like we’re sitting together in RStudio with two screens. We’ll cover:
- What they are and when to use them
- Base R version (quick & built-in)
- ggplot2 version (modern, beautiful, professional — the 2026 standard)
- Horizontal vs vertical
- Stacked, grouped, dodged bars
- Common customizations
- Your mini practice exercise
Let’s begin!
1. What is a Bar Chart? (Clear & Honest Definition)
A bar chart uses rectangular bars to show:
- Categories (on one axis — usually x-axis)
- Values (height or length of bar — usually y-axis)
The length / height of each bar is proportional to the value it represents.
Main purposes:
- Compare magnitude across different categories
- Show counts, totals, averages, percentages per group
- Display rankings (highest to lowest)
- Show composition (stacked bars)
Classic real-life examples:
- Average monthly electricity bill by area in Hyderabad
- Number of students in each class
- Sales by product category
- Vote share by political party
- Website traffic sources (Organic, Direct, Social, Paid)
Golden rule 2026: If you want people to accurately compare values, use a bar chart (not pie, not donut, not bubble unless very special case).
2. Base R Bar Charts – Fast & No Installation Needed
Base R uses barplot() — very simple for quick work.
Example 1 – Simple vertical bar chart
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# Fake data – average electricity bill by area (Hyderabad 2026) areas <- c("Gachibowli", "Hitech City", "Banjara Hills", "Kukatpally", "Uppal") avg_bill <- c(4850, 6200, 7800, 4100, 5300) barplot(avg_bill, names.arg = areas, main = "Average Monthly Electricity Bill – Hyderabad Areas", xlab = "Area", ylab = "Average Bill (₹)", col = c("#FF6F61", "#6B7280", "#FBBF24", "#34D399", "#A78BFA"), border = "white", ylim = c(0, 9000)) # Add value labels on top text(1:length(avg_bill), avg_bill + 200, labels = paste("₹", avg_bill), col = "black", font = 2, cex = 1.1) |
Example 2 – Horizontal bar chart (often better for long labels)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
barplot(avg_bill, names.arg = areas, horiz = TRUE, # ← horizontal! main = "Avg Electricity Bill – Horizontal (easier to read labels)", xlab = "Average Bill (₹)", ylab = NULL, col = "skyblue", border = NA, las = 1) # horizontal text text(avg_bill + 200, 1:length(avg_bill), labels = paste("₹", avg_bill), pos = 4, cex = 1.1) |
3. ggplot2 Bar Charts – The 2026 Professional Standard
ggplot2 is overwhelmingly preferred when the chart needs to look good, be shared, or published.
Install once if needed:
|
0 1 2 3 4 5 6 7 |
# install.packages("ggplot2") library(ggplot2) |
Basic vertical bar chart
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# Data in tidy format (ggplot loves this) bill_df <- data.frame( area = factor(areas, levels = areas), # preserve order bill = avg_bill ) ggplot(bill_df, aes(x = area, y = bill, fill = area)) + geom_col(width = 0.7) + geom_text(aes(label = paste("₹", bill)), vjust = -0.5, size = 4, fontface = "bold") + scale_fill_brewer(palette = "Pastel1") + labs( title = "Average Monthly Electricity Bill – Hyderabad 2026", subtitle = "By Residential Area", x = NULL, y = "Average Bill (₹)" ) + theme_minimal(base_size = 14) + theme(axis.text.x = element_text(angle = 30, hjust = 1), legend.position = "none") # no legend needed when labels are on bars |
Grouped / dodged bar chart (compare two variables)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# Add another variable – summer vs winter average bill_df_long <- data.frame( area = rep(areas, 2), season = rep(c("Summer", "Winter"), each = 5), bill = c(6200, 7100, 8500, 5200, 6500, # summer 3500, 4800, 6200, 3000, 4100) # winter ) ggplot(bill_df_long, aes(x = area, y = bill, fill = season)) + geom_col(position = "dodge", width = 0.7) + # side-by-side bars scale_fill_manual(values = c("Summer" = "#FF6B6B", "Winter" = "#4ECDC4")) + labs( title = "Electricity Bill – Summer vs Winter", x = NULL, y = "Average Bill (₹)", fill = "Season" ) + theme_minimal() + theme(axis.text.x = element_text(angle = 25, hjust = 1)) |
Stacked bar chart (composition)
|
0 1 2 3 4 5 6 7 8 9 10 |
ggplot(bill_df_long, aes(x = area, y = bill, fill = season)) + geom_col(position = "stack") + # stacked = fill the whole height scale_fill_manual(values = c("#FF6B6B", "#4ECDC4")) + labs(title = "Stacked – Total Bill by Season per Area") + theme_minimal() |
4. Quick 2026 Best Practices for Bar Charts
- Horizontal bars → when category names are long
- Order bars → highest to lowest (use reorder() in ggplot)
- Labels on bars → geom_text() or geom_label() — much better than legend
- Avoid 3D bars — they distort perception
- Start y-axis at 0 → never truncate unless you clearly explain why
- Use meaningful colors — avoid rainbow unless categories are unrelated
- Add title + subtitle + source note
- Use geom_col() (not geom_bar()) when you already have the heights calculated
Your Mini Practice Right Now
Copy this ggplot2 code — then try:
- Change to horizontal (coord_flip())
- Order areas from highest to lowest bill
- Remove legend and put values directly on bars
- Try position = “fill” (100% stacked)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
library(ggplot2) df <- data.frame( area = c("Gachibowli", "Hitech City", "Banjara Hills", "Kukatpally", "Uppal"), summer_bill = c(6200, 7100, 8500, 5200, 6500), winter_bill = c(3500, 4800, 6200, 3000, 4100) ) |> tidyr::pivot_longer(cols = ends_with("_bill"), names_to = "season", values_to = "bill") |> mutate(season = sub("_bill", "", season)) ggplot(df, aes(x = area, y = bill, fill = season)) + geom_col(position = "dodge") + geom_text(aes(label = paste("₹", bill)), position = position_dodge(width = 0.9), vjust = -0.4, size = 3.5) + scale_fill_manual(values = c("summer" = "#FF6B6B", "winter" = "#4ECDC4")) + labs(title = "Electricity Bill Comparison – Summer vs Winter", x = NULL, y = "Average Bill (₹)") + theme_minimal() + theme(axis.text.x = element_text(angle = 30, hjust = 1)) |
Which version do you think communicates the comparison fastest?
Want to go deeper?
- Grouped vs stacked vs 100% stacked in detail
- Lollipop charts (modern alternative to bars)
- Error bars on bar charts (mean ± sd)
- Saving high-resolution bar charts for reports?
Just tell me — whiteboard is ready! 📊📈🚀
