Everything in R Is a Vector
Most languages have scalars - single values - and then some container type for holding several of them. R skips the scalar. A vector is an ordered collection of values that all share one type, and it is the unit everything else is built on. Even what looks like a lone number is a vector of length 1:
c() - short for combine - is how you build a vector by hand. length() tells you how many elements it holds. And 5 on its own reports is.vector as TRUE with a length of 1: R never stops working with vectors, it just sometimes works with very short ones.
This design is why R code looks different from Python or JavaScript: operations you would write a loop for elsewhere apply to whole vectors in one expression. We'll get to that below.
One rule to internalize now: all elements of a vector have the same type. If you hand c() a mix, it doesn't complain - it silently converts everything to the most flexible type present:
A string in the mix turns everything into strings ("1", "TRUE"). Logicals among numbers become 1 and 0. This coercion follows a fixed pecking order - logical → integer → double → character - and it happens without a warning, which is why a stray "7" in a column of numbers can quietly turn your whole dataset into text. The full hierarchy is covered in data types; if you need to hold genuinely mixed values, that's what lists are for.
Generating Sequences: :, seq() and rep()
Typing every element into c() gets old fast. R has three shortcuts for regular patterns:
1:10is the everyday one: integers from one bound to the other, in steps of 1.seq()generalizes it: pick the step withby =, or say how many elements you want withlength.out =and let R work out the spacing.seq_len(n)produces1throughnand is the safe way to build loop indices - unlike1:n, it correctly gives an empty vector whennis 0 instead of counting1 0backwards.rep()repeats:times =repeats the whole vector end to end (1 2 1 2 1 2),each =repeats every element in place (1 1 1 2 2 2). The two are easy to confuse; run the snippet and compare.
Indexing: R Counts From 1
Square brackets pull elements out. The first element is [1] - not [0]. If you're arriving from Python or JavaScript, this is the single most common early stumble:
Three things here worth pausing on:
- You can index with a vector of positions -
fruits[c(2, 4)]grabs the second and fourth in one go. - Negative indices mean "everything except."
fruits[-1]is the vector without its first element. This is completely different from Python, where-1means the last element. You can't mix positive and negative indices in the same call. fruits[0]isn't an error - it returns an emptycharacter(0). An off-by-one bug in R often produces silent empty results rather than a crash, so it pays to checklength()when something downstream looks mysteriously blank.
Elements can also carry names, which gives you a third way to index - by label:
Named vectors work like a lightweight lookup table and keep code readable: prices["tea"] says what it means, prices[2] doesn't.
Logical Masks and which()
The most powerful way to index is with a logical vector - a mask of TRUE/FALSE values the same length as the data. Every comparison on a vector produces exactly that:
temps > 24 doesn't give one answer - it gives five, one per element. Putting that mask inside [ ] keeps the elements where the mask is TRUE. Combine conditions with & (and) and | (or), covered with the rest of the operators.
which() translates a mask into positions: here 2 3 5, the indices of the hot readings. Reach for it when you need to know where the matches are - to report them, or to index a different vector at the same spots. For plain filtering, temps[temps > 24] is more direct than temps[which(temps > 24)].
This filter-with-a-mask pattern is the backbone of practically all data work in R - it comes straight back when you filter rows of a data frame.
Vectorized Math and the Recycling Rule
Arithmetic in R applies element by element across whole vectors - no loop required:
The first line is the headline feature: prices * 2 doubles every element. Where other languages need a for loop, R needs an operator - and the vectorized form is both shorter and faster, because the looping happens in optimized C underneath. Write a for loop when each step depends on the previous one; for element-wise math, vectorize.
The third line shows the recycling rule: when the vectors have different lengths, R repeats the shorter one to match. c(10, 20) is recycled to 10 20 10 20, giving 11 22 13 24. Recycling 2 across a length-3 vector is exactly what made prices * 2 work - a scalar is just a length-1 vector being recycled.
Recycling is clean when the longer length is an exact multiple of the shorter. When it isn't, R still computes an answer - and emits a warning:
You get 11 22 13 plus a warning that the longer object length is not a multiple of the shorter object length. Treat that warning as a bug report: partial recycling is almost never what you meant, and it usually means two vectors that should have been the same length aren't.
Sorting, Reversing, Deduplicating
Three small utilities you'll use constantly:
sort() orders the values, rev() flips the existing order without sorting, and unique() drops duplicates while keeping first appearances. None of them modify x - like almost everything in R, they return a new vector and leave the original alone.
What You Take Away
- A vector is R's fundamental unit: ordered, one type throughout, and even single values are length-1 vectors.
- Build with
c(), generate patterns with:,seq(), andrep()- and rememberc()silently coerces mixed types. - Indexing starts at 1; negative indices drop elements; names let you index by label.
- Logical masks (
x[x > 5]) are the filtering idiom of the whole language;which()turns a mask into positions. - Math is vectorized, shorter vectors get recycled, and a partial-recycling warning means you have a bug.
Next up: lists - the container for when your values don't all share one type.
Frequently Asked Questions
How do you create a vector in R?
With the c() function (short for combine): x <- c(10, 20, 30). For regular sequences, use 1:10, seq(0, 1, by = 0.25), or rep(0, 5). Every element must end up the same type - if you mix types, R silently converts them all to the most flexible one.
Does R indexing start at 0 or 1?
At 1. x[1] is the first element, x[length(x)] is the last. This trips up everyone coming from Python, JavaScript, or C - there is no element x[0] (asking for it returns an empty vector, not an error, which makes the bug quiet).
What does the recycling rule in R do?
When you do arithmetic on two vectors of different lengths, R repeats the shorter one until it matches the longer one. c(1, 2, 3, 4) + c(10, 20) gives 11 22 13 24. If the longer length isn't a multiple of the shorter one, R still computes an answer but emits a warning - treat that warning as a bug.
What does which() do in R?
It converts a logical vector into the positions of its TRUE values. If x > 5 gives FALSE TRUE TRUE, then which(x > 5) gives 2 3. Use it when you need the positions themselves; for plain filtering, indexing with the logical vector directly (x[x > 5]) is simpler.