3 Coin Objects

3.1 Introduction

In this chapter we describe how to create object classes in R. Specifically, we will focus on the so-called S3 classes or S3 system. This is one of the three types of Object Oriented (OO) systems available in R, and it is the most common among R packages.

3.2 Objects and Classes

In the previous chapter we learned how to create a toss() function, and also how to document it with roxygen comments. We can invoke toss() to generate a first series of five tosses, and then compute the proportion of heads:

We can also get a second series of tosses, but this time involving tossing a coin six times. Similarly, we compute the proportion of heads:

The above code works … except that there is an error; the number of heads in six is being divided by 5 instead of 6. R hasn’t detected this error: it doesn’t know that the division has to be done using length(six).

Wouldn’t it be prefarable to have some mechanism that prevented this type of error from happening? Bugs will always be part of any programming activity, but it is better to minimize certain types of errors like the one above.

3.3 S3 Classes

R has two (plus one) object oriented systems, so it can be a bit intimidatin gwhen you read and learn about them for the first time. The goal of this tutorial is not to make you an expert in all R’s OO systems, but to help you become familiar with the so-called S3 class.

S3 implements a style of object oriented programming called generic-function OO. S3 uses a special type of function called a generic function that decides which method to call. Keep in mind that S3 is a very casual system: it does not really have a formal definition of classes.

S3 classes are widely-used, in particular for statistical models in the "stats" package. S3 classes are very informal in that there is not a formal definition for an S3 class. Usually, S3 objects are built on top of lists, or atomic vectors with attributes. But you can also turn functions into S3 objects.

Note that in more formal OOP languages, all functions are associated with a class, while in R, only some are.

3.3.1 Making an object

To make an object an instance of a class, you just take an existing base object and set the "class" attribute. You can do that during creation of the object with structure()

You can also create an object first, and then specify its class with the function class():

As any object in R, you can inspect the class coin1, and coin2 with the class() function:

You can also determine if an object inherits from a specific class using inherits()

Having a "coin" object, we can pass it to the toss() function to simulate flipping the coin:

3.4 A more robust "coin" class

Let’s review our class "coin". The way we defined a coin object was like this:

While this definition is good to illustrate the concept of an object, its class, and how to define generic methods, it is a very loose-defined class. One could create a "coin" out of c('tic', 'tac', 'toe'), and then use toss() on it:

We need a more formal definition of a coin. For instance, it makes more sense to require that a coin should only have two sides. In this way, the vector ttt would not be a valid coin.

For convenience purposes, we can define a class constructor function to initialize a "coin" object:

3.5 Improving "coin" objects

To implement the requirement that a coin must have two sides, we can add an if condition to check for the length of the input vector:

Let’s try our modified constructor function coin() to create a virtual version of the US penny like the one in the image below:

US Penny (www.usacoinbook.com)

Figure 3.1: US Penny (www.usacoinbook.com)

Now let’s try coin() with an invalid input vector. In this case, the constructor function will stop() execution with an error message because the input argument has more than 2 elements.

Because the toss() function simulates flips using sample(), we can take advantage of the argument prob to specify probabilities for each side of the coin. In this way, we can create loaded (i.e. biased) coins.

The way we are going to keep the probability of each side of the coin is with the use an objetc’s attributes. An example of an attribute is the class of an object. For example the class of our "coin" objects:

Notice how everytime you print the name of a "coin" object, its class is displayed in the form of attr(,"class").

3.5.1 Attributes

In addition to the class attribute of a coin, the idea is to assign another attribute for the probability values. We can do this by adding a prob argument to the constructor function, and then pass it as an attribute of the coin object inside the class-constructor function.

In the previous code, the prob argument takes a vector of probabilities for each element in object. This vector is passed to object via the function attr() inside the body of coin(). Notice the use of a default prob = c(0.5, 0.5), that is, a fair coin by default.

3.5.2 Using a list

Another way to implement a constructor function coin() that returns an object containing values for both the sides and the probabilities, is to use an R list. Here’s the code for this option:

3.5.3 Auxiliary Checker

Once again, we need to check for the validity of prob. We basically need to check that prob and its elements meet the following requirements:

  • must be numeric and of length 2
  • probability values must be between 0 and 1
  • the sum of these values must add up to 1

Here is one possible function to check the aspects of prob listed above:

Note that I’m adding a TRUE statement at the end of the function. This is just an auxiliary value to know if the function returns a valid prob. Now let’s test it with valid and invalid values:

Here’s the improved constructor function coin():

3.7 Extending classes

We can extend the class "coin" and create a derived class for special types of coins. For instance, say we want to create a class "quarter". One side of the coin refers to George Washington, while the other side refers to the bald eagle:

https://en.wikipedia.org/wiki/Quarter_(United_States_coin)

We can create a quarter by first starting with a coin() of sides washington and bald-eagle, and then assign a "quarter" class:

Interestingly, our coin quarter1 inherits from "coin":

In other words, quartier1 is of class "quarter" but it is also a "coin" object.

Likewise, we can create a class for a slightly unbalanced "dime":

Here’s another coin example, a peso, from Mexico (where I grew up). When you flip a peso, mexicans will talk about two sides: aguila (eagle) or sol (sun):


Make a donation

If you find this resource useful, please consider making a one-time donation in any amount. Your support really matters.