Skip to main content
v2026.11,858 entries · CC-BY 4.0

Barplot in R: barplot(), geom_bar(), geom_col()

How to make a barplot in R with barplot(), geom_bar() or geom_col(): choose by data shape, group and stack bars, read the output, and fix common problems.

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:

  1. Install the whole tidyverse: install.packages("tidyverse")
  2. 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.

  1. Count the categories. barplot() expects heights, not raw rows, so tabulate first: counts <- table(mtcars$cyl)
  2. Draw the bars: barplot(counts). Because counts is a named vector, the bar labels come from its names. The R manual states that when names.arg is omitted, labels are taken from the names of a vector or the column names of a matrix.
  3. Add labels and a title: barplot(counts, main = "Cars by cylinders", xlab = "Cylinders", ylab = "Count")
  4. Make the bars horizontal with horiz = TRUE (default FALSE) and set a fill with col (default is “grey” when height is a vector).

How to make a bar plot in R with ggplot2

  1. Load the package: library(ggplot2)
  2. Let ggplot2 count the rows: ggplot(mtcars, aes(x = factor(cyl))) + geom_bar()
  3. 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.

Follow CASRAI

Research-administration guidance, standards updates and independent tool reviews.

Ask CASRAI · free to try

Ask about Barplot in R: barplot(), geom_bar(), geom_col()

Ask your first 2 questions free below. Subscribers get 150 a day for $29 a month.

An AI assistant specialized in research administration. It cites the sources behind every answer, labels web answers and says when it can't answer.

Answers draw on CASRAI's guides and dictionary plus the federal and funder documents we index: Federal Register, Grants.gov, Regulations.gov and UKRI.

Works on this site and inside Claude, Cursor and the AI tools you already use.

Everything CASRAI publishes — this page, the dictionary, the guides and the news — stays free to read, with no account and no card.

Referenced across the research world

University of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logoUniversity of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logo
  • University of Cambridge logo
  • Columbia University logo
  • Crossref logo
  • University of Edinburgh logo
  • Harvard University logo
  • University of Oxford logo
  • Princeton University logo
  • Stanford School of Medicine logo
  • University College London logo
  • ORCID logo

View CASRAI adoption →

Ask CASRAI · Regulatory Radar

Research-admin question? Get an answer that links its sources.

An AI assistant specialized in research administration. Every answer links its sources to check before you act. 2 questions free, no account. $29/month after.

  • Answers draw on CASRAI's guides and dictionary plus the federal and funder documents we index: Federal Register, Grants.gov, Regulations.gov and UKRI.
  • Every answer numbers its sources and links each one, so you can check the source yourself.