Lecture 03
The syntax is nearly identical. The key difference is that R’s comparison operators are vectorized (element-wise, returning a logical vector), whereas Python compares two objects and returns a single bool.
| Comparison | R | Python |
|---|---|---|
| less than | x < y |
x < y |
| greater than | x > y |
x > y |
| less than or equal to | x <= y |
x <= y |
| greater than or equal to | x >= y |
x >= y |
| equal to | x == y |
x == y |
| not equal to | x != y |
x != y |
| membership | x %in% y |
x in y |
R compares element by element, while Python compares the objects as a whole and uses in for membership.
Both languages can order strings, but differently - Python compares by Unicode code point (so ASCII A-Z sort before ASCII a-z), while R uses the collation rules of the current locale (roughly alphabetical, with case as a tie breaker).
Ordered comparisons of strings, lists, and tuples in Python, are lexicographic (dictionary order) - elements are compared pairwise from the front and the first difference decides the result. If one sequence runs out first, it sorts before the longer one.
| Operation | R (vectorized) | R (scalar) | Python |
|---|---|---|---|
| and | x & y |
x && y |
x and y |
| or | x | y |
x || y |
x or y |
| not | !x |
not x |
|
| exclusive or | xor(x, y) |
x != y |
In R you choose between the vectorized and scalar forms; in base Python and / or are always scalar, though with lists they can misleadingly look vectorized.
&& / || in R and and / or in Python evaluate their left operand first and only evaluate the right operand if the left one does not already decide the outcome.
This makes them useful as guards, where an earlier condition rules out inputs that would cause a later condition to error.
Python’s and and or accept any values, not just bools. They check the truthiness of x and short-circuiting decides which value comes back:
x and y - if x is falsy the result is x, otherwise it is y
x or y - if x is truthy the result is x, otherwise it is y
Either way the result is one of the original values, unchanged, whereas R’s && and || always produce a single TRUE, FALSE, or NA.
is a common Python idiom for supplying a default when x is falsy. R 4.4 added the null coalescing operator %||% for the narrower case where x is NULL.
if and elseR wraps the condition in () and the body in {}, while Python ends the condition with : and the body is the following indented block.
else if and elifConditions are checked in order and only the first true branch runs; the optional else branch runs if no other branch triggered.
R’s if is an expression that returns the value of the evaluated branch, so it can be used directly in an assignment. Python’s if is a statement that returns nothing; instead there is a separate conditional expression, a if cond else b.
Since R 4.2, if throws an error if the condition has length > 1 (older versions used the first value with a warning).
Python does not automatically apply the comparison element-wise - x == 1 is simply False, and any non-empty list is truthy regardless of its contents.
Both languages provide any() and all() for reducing multiple logical values to a single one.
Python’s conditionals accept any object and use its truthiness. R’s if requires a single logical value, though it will coerce other values that as.logical() recognizes.
[1] "yes"
[1] "no"
[1] "yes"
R’s ifelse() is the element-wise counterpart to if - given a logical vector it returns a vector of the same length built from the yes and no arguments.
Base Python has no equivalent; the idiomatic approach is a list comprehension with a conditional expression (next time).
R - NA is sticky in comparisons and if errors on NA rather than guessing. any() and all() follow the | and & rules.
Python - None supports equality tests, but ordering it with a number raises TypeError. For nan, ordered comparisons and == are False; != is True; and despite comparing equal to nothing, nan is truthy.
Check for missing values directly with is.na(), x is None, or math.isnan().
R’s switch() selects a branch based on a character (or integer) value, while Python 3.10+ has the match statement (which also supports much more general structural pattern matching).
R has several ways of communicating with the user beyond print() / cat(), each of which is a condition that can be handled programmatically:
message() - diagnostic messages (sent to stderr)
warning() - something unexpected but not fatal, execution continues
stop() - an error, execution halts
Python has rough equivalents in print() (or the logging module), warnings.warn(), and raise with an exception object.
Both languages also have a shorthand for checking assumptions - R’s stopifnot() and Python’s assert statement:
Python exceptions are objects with a class hierarchy - the class describes what went wrong and allows handling to be selective. R errors are, by default, all of the same class (simpleError) and are distinguished only by their message.
R’s try() evaluates an expression and, instead of halting, returns a try-error object if an error occurs. Python’s try / except block runs the except code only if a matching exception was raised in the try body.
Without running the code, what do you expect the output (or error) to be for each of the listed values of x?
for loopsR’s for iterates over the elements of a vector (or list), while Python’s for iterates over the elements of any iterable object (lists, tuples, strings, ranges, dictionaries, files, …).
Loops over indices need a sequence of integers - R has :, seq(), seq_len(), and seq_along(), while Python has range().
The R idiom is seq_along(x). Python’s equivalent is range(len(x)), but enumerate() is preferred as it yields the index and the value together (as a tuple, which is unpacked into i and v below).
1:length(x)The common R idiom 1:length(x) fails for empty vectors since 1:0 is c(1, 0) - use seq_along() or seq_len() instead. Python’s range(len(x)) is safe since range(0) is empty.
Python’s zip() iterates over multiple sequences together, stopping at the shortest. R has no direct equivalent - index with seq_along() instead (though vectorization usually makes this unnecessary).
while loopsRepeat the body as long as the condition is TRUE / truthy - the condition is checked before each iteration, so the body may never run.
break and next / continuebreak exits the (innermost) loop entirely, while next in R and continue in Python skip the rest of the current iteration.
else and passTwo Python-only constructs - loops can have an else clause that runs when the loop finishes without a break, and pass is a no-op placeholder for where a statement is syntactically required.
Growing an R vector with c() copies it every iteration (quadratic runtime), so for longer loops preallocate (numeric(n), character(n), vector("list", n)) and assign by index. Python’s list.append() is amortized constant time, so appending is idiomatic.
To the right are vectors containing all prime numbers between 2 and 100 and some values x we would like to check for primality.
Using nested loops, write code in both R and Python that prints only the values of x that are not prime - without using subsetting, %in%, or in.
In Python, try using the loop else clause; in R you will need a flag variable.
| Construct | R | Python |
|---|---|---|
| conditional | if / else if / else |
if / elif / else |
| conditional expression | if (cond) a else b |
a if cond else b |
| vectorized conditional | ifelse() |
comprehension, np.where() |
| multi-way branch | switch() |
match / case |
| for loop | for (x in vec) {} |
for x in iterable: |
| while loop | while (cond) {} |
while cond: |
| infinite loop | repeat {} |
while True: |
| skip iteration | next |
continue |
| exit loop | break |
break |
| integer sequences | :, seq_len(), seq_along() |
range() |
| index + value | seq_along() + x[i] |
enumerate() |
| multiple sequences | seq_along() + indexing |
zip() |
| reduce logicals | any(), all() |
any(), all() |
| blocks | { } |
: + indentation |
| Concept | R | Python |
|---|---|---|
| message | message() |
print(), logging |
| warning | warning() |
warnings.warn() |
| error | stop() |
raise SomeError() |
| assertion | stopifnot() |
assert |
| catch | try(), tryCatch() |
try / except |
| always run | tryCatch(finally = ), on.exit() |
finally |
| error object | condition (simpleError) |
exception (subclass of Exception) |
| error message | conditionMessage(e) |
str(e) |
R’s comparison and logical operators (==, &, |) are vectorized; && / || in R and and / or in Python are scalar and short-circuit.
if in R needs exactly one TRUE / FALSE - use any(), all(), or ifelse() for vectors. Python’s if tests the truthiness of any object.
Loop syntax is nearly identical - the differences are blocks ({} vs indentation), seq_along() vs range() / enumerate(), and next vs continue.
Errors are conditions in R (stop(), tryCatch()) and typed exception objects in Python (raise, try / except).
Sta 523 - Fall 2026