Written and maintained by CASRAI Editorial Board
Last updated
To make a barplot in R, count your categories with table() and pass the result to barplot() in base R, or call geom_bar() in ggplot2 and let it count the rows for you. The choice that trips most people up is not the syntax but the shape of the data: barplot() and geom_col() draw heights you already have, while geom_bar() computes the heights by counting. This guide covers bar plots in R (the same thing as a bar plot or barplot) in both systems, with the argument behaviour taken from the official R manual and the ggplot2 reference.
What it is used for in research
A bar plot compares a quantity across discrete groups: the number of participants per treatment arm, the count of records per category, or a summary value such as a mean per condition. Each group gets one rectangular bar. When the variable is continuous and you want to see its distribution, a histogram is the better chart; see histograms in R, and the chart-type overview in types of graphs in statistics. If your grouping variable is categorical, these categorical variable examples show what qualifies.
Install or access (official source only)
barplot() belongs to the graphics package documented in the official R manual (graphics::barplot), so it needs no extra installation. ggplot2 is a separate package. The ggplot2 site (ggplot2.tidyverse.org) gives two ways to install it:
- Install the whole tidyverse:
install.packages("tidyverse") - Install just ggplot2:
install.packages("ggplot2")
Then load it with library(ggplot2).
Step-by-step: make a bar plot from raw categories
How to make a barplot in R with base R
The examples use mtcars, a data frame that ships with R, so you can run them as written.
- Count the categories.
barplot()expects heights, not raw rows, so tabulate first:counts <- table(mtcars$cyl) - Draw the bars:
barplot(counts). Becausecountsis a named vector, the bar labels come from its names. The R manual states that whennames.argis omitted, labels are taken from the names of a vector or the column names of a matrix. - Add labels and a title:
barplot(counts, main = "Cars by cylinders", xlab = "Cylinders", ylab = "Count") - Make the bars horizontal with
horiz = TRUE(defaultFALSE) and set a fill withcol(default is “grey” whenheightis a vector).
How to make a bar plot in R with ggplot2
- Load the package:
library(ggplot2) - Let ggplot2 count the rows:
ggplot(mtcars, aes(x = factor(cyl))) + geom_bar() - If your data frame already holds the heights, use
geom_col()instead:ggplot(df, aes(x = group, y = value)) + geom_col()
The ggplot2 reference puts the rule in one line: geom_bar() makes the height of the bar proportional to the number of cases in each group, and if you want the heights to represent values in the data, use geom_col() instead. geom_col() uses stat_identity(), which leaves the data as is.
Grouped and stacked bars
Base R: vector versus matrix
When height is a vector you get one sequence of bars. When it is a matrix, the manual says beside = FALSE (the default) makes each bar correspond to a column of height, so the bars are stacked, while beside = TRUE places the values in each column next to each other instead. A two-way table is a matrix in this sense:
tab <- table(mtcars$cyl, mtcars$gear)barplot(tab) (stacked)barplot(tab, beside = TRUE, legend.text = rownames(tab)) (side by side, with a legend)
Two details from the manual are easy to miss. First, legend.text is only useful when height is a matrix. Second, the space default is 0.2, but for a matrix with beside = TRUE it defaults to c(0, 1): no gap inside a group, a full bar-width gap between groups.
ggplot2: stack, dodge or fill
Per the reference, multiple bars at the same x position are stacked by default through position_stack(). To put them side by side, use position_dodge() or position_dodge2(); position_fill() shows relative proportions at each x.
ggplot(mtcars, aes(x = factor(cyl), fill = factor(gear))) + geom_bar(position = "dodge")
Output and how to read it
A bar’s height is a count if you used table() or geom_bar(), and the supplied value if you used geom_col() or a numeric vector, so label the y axis accordingly. Compare bars by length from a shared baseline. Stacked bars make only the bottom segment easy to compare across groups; dodged bars make every group comparable; position_fill() trades counts for proportions, so the bars all reach the same height and you can no longer read group sizes from them.
Two habits keep bar plots honest. Start the value axis at zero, because bar length encodes the value. And if the bars show means, a bar alone hides spread, so consider showing an interval alongside it; the interquartile range in R guide covers one measure of spread.
barplot() also returns a value. The manual describes it as the bar midpoints: a numeric vector, or a matrix when beside = TRUE, where colMeans(mp) gives the group midpoints. Store it (mp <- barplot(counts)) when you want to add text or error bars at the right x positions. For the same chart built in a menu-driven package, see bar charts in SPSS.
Troubleshooting
My geom_bar() bars are counts, not my y values
geom_bar() defaults to stat = "count" and computes the height itself. If your y column already contains the heights, switch to geom_col(), which is the documented choice for bars that represent values in the data.
My bars have no labels
Labels come from the names of a vector or the column names of a matrix. Pass names.arg explicitly, or give your vector names, or use table() which supplies them.
The legend does not appear
legend.text is only useful when height is a matrix. With a plain vector there is nothing to key.
Changing width does nothing
The manual notes that a single width value has no visible effect unless xlim is also set. Use space to change the gaps.
My groups are stacked and I wanted them side by side
Set beside = TRUE in barplot(), or position = "dodge" (or position_dodge()) in ggplot2.
Free alternatives: base R, geom_bar() and geom_col()
| Function | What sets the bar height | Grouping behaviour |
|---|---|---|
barplot() (base R graphics) |
The values you pass as height (use table() for raw categories) |
Matrix input: stacked by default, side by side with beside = TRUE |
geom_bar() (ggplot2) |
Number of cases in each group (stat = "count") |
Stacked by default; position_dodge() or position_fill() to change |
geom_col() (ggplot2) |
Values in the data, left as is (stat_identity()) |
Stacked by default; same position options |
FAQ
How do I make a bar plot in R?
For raw categories, run barplot(table(x)) in base R or ggplot(data, aes(x)) + geom_bar() in ggplot2. For values you already have, pass them to barplot() or use geom_col().
Are bar plots in R, a bar plot in R and a barplot in R the same thing?
Yes, they are different spellings of the same chart. In base R the function is spelled barplot(); in ggplot2 the geoms are geom_bar() and geom_col().
What is the difference between a bar plot and a histogram?
A bar plot has one bar per category of a discrete variable; a histogram bins a continuous variable. See histograms in R.
Should I use barplot() or ggplot2?
Both draw the same chart. barplot() needs no extra package; ggplot2 needs one install and gives you the stack, dodge and fill position options by name.
Sources (official docs)
Checked 2026-10-29.
- R manual,
graphics::barplot: https://stat.ethz.ch/R-manual/R-devel/library/graphics/html/barplot.html - ggplot2 reference,
geom_bar()andgeom_col(): https://ggplot2.tidyverse.org/reference/geom_bar.html - ggplot2 installation instructions: https://ggplot2.tidyverse.org/








