Skip to Content
Course content

96: Attributes and Custom Object Classes

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Think of an R object like a shipping package. The "data" is the actual item inside the box—say, a pair of shoes. But the box itself has labels stuck to the outside: the shipping address, the weight, a "Fragile" sticker, and a tracking number. These labels aren't the shoes, but they provide critical context about how to handle the shoes. If a handler sees the "Fragile" label, they treat the box differently than if it were labeled "Indestructible."

In R, these labels are called attributes. Most of the time, you use attributes without realizing it. When you create a data frame, R is actually just creating a list and slapping a "data.frame" label on the class attribute. That label is what tells R, "Hey, don't treat this like a normal list; treat it like a table."

Adding metadata to your data

You can attach your own custom attributes to almost any object using the attr() function. Let's say you're recording temperature readings. A raw number like 22.5 is useless if you don't know if it's Celsius or Fahrenheit. Instead of creating a complex list, you can just attach the unit as an attribute.

temp <- 22.5
attr(temp, "unit") <- "Celsius"
attr(temp, "location") <- "Lab A"

# To see all the labels on our "box"
attributes(temp)

I've found that using attributes is a great way to keep your data "clean" while still carrying the context you need. It keeps the actual value as a simple numeric type, so your math doesn't break, but the metadata is right there if you need to query it later.

Teaching R how to handle your custom objects

Now, here is where it gets powerful. If you set an attribute called class, you've officially created a custom object class. This is the foundation of R's S3 system. By giving an object a class, you're telling R, "Whenever a generic function is called on this object, look for a specific version of that function designed for this class."

Let's turn our temperature variable into a TempReading object. Right now, if you print it, R just shows 22.5. That's boring and potentially dangerous if you forget the units.

class(temp) <- "TempReading"

By itself, this doesn't change how the object looks. But now we can write a method specifically for the print function that targets TempReading objects.

The magic of generic functions

In R, print() is a "generic" function. When you call it, R looks at the class of the object and searches for a function named print.className. If it finds one, it uses it; otherwise, it falls back to the default.

Check this out:

print.TempReading <- function(x, ...) {
  unit <- attr(x, "unit")
  loc <- attr(x, "location")
  cat(sprintf("Temperature: %.1f %s (Recorded at: %s)\n", x, unit, loc))
}

# Now try printing the object again
print(temp)

Suddenly, print(temp) doesn't just output a number; it outputs a formatted string. I love this pattern because it allows you to keep your data structures simple while making the representation of that data highly professional and informative. You aren't changing the value; you're just changing how R talks about it.




📋 Practical Task

Building a Currency Conversion Object

Your task is to create a custom R class that handles monetary values. A simple number isn't enough because we need to know which currency we are dealing with.

  • Create a numeric variable balance with a value of 150.75.
  • Assign it two attributes: "currency" (e.g., "USD", "EUR", or "GBP") and "account_holder" (a name of your choice).
  • Assign the class "Money" to this object.
  • Write a custom print.Money method that prints the balance formatted with the currency symbol and the account holder's name.
    Example Output: "Account: Jane Doe | Balance: 150.75 USD"
  • Test your implementation by calling print(balance).
Rating
0 0

There are no comments for now.

to be the first to leave a comment.