# 2.1.2 Objects {#chap:obje}

## Take-home Messages

In R, everything that exists is an object:

- There are four common user-defined atomic vector _types_ you'll encounter in R.
- Vectors are 1-dimensional collections of 0 or more elements of a common data type.
- Data frames are 2-dimensional collections of 0 or more vectors.
- The _class_ of an object dictates how it will be handled by different functions.

Objects are anything which^[In R, _objects_ are synonymous with _variables_ of other programming languages.] can be assigned a name. They can refer to a wide variety of different data types, including constants, various data structures, functions or graphs.^[_Objects_ are like files on your computer: They contain a wide variety of different data structures, can be modified, and their name may not relate to their contents. However, the extension of a file, like the class of an object, tells us about how we should handle this file. e.g. Which program will open it?] Work in R is centered around objects and their classes, and as such is referred to as an _object-oriented language_.
^[Object names can contain letters, numbers, `.` or `_`, as long as it starts with a letter, or `.` followed by a letter. Note that object names are case sensitive.]

R can store data in a variety of formats. In this workshop we will focus on the two most commonly used structures: vectors and data frames. However, as your R programming skills develop, you will likely encounter lists, matrices and arrays. These are described later in the chapter but will not be dealt with in class. The differences between the main data structures in R can be summarised as:^[Although lists, matrices and arrays are presented here, it is worthwhile to return to this section after we have completed the workshop and explored working with data frame in depth, including indexing and _Split-Apply-Combine_. At that point you should be able to better understand how these data structures are handled in R.]

Table: (\#tab:first-functions) A summary of common data structures in R.

| Name       | Composition   | Dimensions |
|:----------:|:-------------:|:----------:|
| Vector     | Homogeneous   | 1          |
| Matrix     | Homogeneous   | 2          |
| Array      | Homogeneous   | n          |
| List       | Heterogeneous | 1          |
| Data frame | Heterogeneous | 2          |

In addition to these 5 data structures, we'll explore some fundamental properties of objects, such as _type_, _attributes_, and _class_, which will help you understand how R deals with objects later on.

## Homogeneous Data-types

### Vectors {#sub:vect}

Vectors are one-dimensional groups of individual data values. We have already encountered vectors on page \@ref(foo) when we defined `foo1` and `foo2`. Remember that these objects only contain the numerical series that we generated with the `seq()` function:

![](figures/Vectors.png)

```{r}
foo1
foo2
```

**An important property of vectors, is that they can only contain one data type**, thus they are _homogeneous_. There are _many_ different data types in R. There are 6 user-defined _atomic vector types_.^[The remaining two, `complex` and `raw` are unlikely to be encountered by beginners and we will not discuss them.] The four most common are:

Table: (\#tab:data-modes) Common data types. Note that numerical vectors are also referred to as double, because of the way they are stored. This is a general term in computing and not specific to R.

| Typ         | Description                                      |
|-------------|--------------------------------------------------|
| `Logical`   | TRUE/T/1 or FALSE/F/0                            |
| `Integer`   | Whole numbers                                    |
| `Double`    | Numeric (i.e. real numbers, including fractions) |
| `Character` | Words                                            |

The `numeric` class refers to either `double` or `integer`. To find out the _type_ of a vector, we can use the `typeof()` function.


```{r}
typeof(foo2)
```

You may wonder why the type of `foo2` is `double` (aka numeric) and not `integer`. This is due to how R predicted the type of data. If we want to force these values to be logical we'd have to use the special integer signifier `L`:

```{r}
foo1 <- seq(1L, 100L, 7L)
typeof(foo1)

```

For the most part this will not make a difference in your work, but you may encounter problems when doing math on type `double` due to floating point errors, discussed on page \@ref(sec:float). For our purposes, we are not bothered if an `integer` is stored as a `double`.

We can use `c()` to create vectors of other data types:

^[Notice that R automatically assigns a type to each vector. A vector's type determines what can be done with the data. Wrongly classified vectors are one of the most common causes of problems when programming in R!]

```{r}
foo3 <- c("Liver", "Brain", "Testes", "Muscle",
"Intestine", "Heart")
typeof(foo3)

foo4 <- c(TRUE, FALSE, FALSE, TRUE, TRUE, FALSE)
typeof(foo4)

```


#### Atomic vector type hierarchy and coercion

When we assign a vector to an object, R automatically determines the atomic vector type.^[Recall that we can influence this, as we saw above, using `L` to force an `integer` type.] The atomic vector types have a hierarchical organisation according to increasing complexity: logical < integer < double < character. This kind of makes sense since logical can only be 0 or 1, and character can be everything. This means that R defaults to the highest level in the hierarchy.

```{r}
test <- c(1:10, "bob")
test
typeof(test)
```

Since `test` is a character vector, I can't do math on it!

```{r}
mean(test)
```

A family of _coercion_ functions are used to coerce one type to another.

```{r}
as.numeric(test)
```

Everything that looked like a number is converted to a number, but the contaminating character is replaced by an NA. Now I can do math, but I need to accommodate for the missing values.^[Note that we can't coerce a character to a logical vector, but if we did only have 0s and 1s, we could.]

```{r}
mean(as.numeric(test), na.rm = T)
```

```{r eval = FALSE, echo = FALSE}
# Doesn't work
as.logical(test)

# Only 0s and 1s
test <- c(0, 1)
typeof(test)
as.logical(test)
```

We can determine the atomic vector type of an object by using `typeof()`, but sometimes we want to ask specifically if an object is of a specific type, in particular when we shift from interactive programming to scripting and writing our own functions.^[You'll notice in the auto-complete that there are _many_ `is.` functions, which can test for more than use atomic vector types. In the examples here, the output length is always 1, since we are just testing for the type of an object, but that is not always the case. e.g. see `is.na()` on page \@ref(sec:missing).] For this purpose there is a long list of `is.` functions that all return a logical vector.

```{r}
is.numeric(foo1)
is.integer(foo2)
is.character(foo3)
is.logical(foo4)
```

### Matrices

A matrix is a 2-dimensional vector, which means every element needs to be the same type. Matrices are incredibly convenient if you have a 2-dimensional grid of numbers on which you want to apply a mathmatical function, like linear algebra. To understand how matrices work, it's useful to get familiar with attributes.

![](figures/Matrices.png)

#### Setting object attributes

Almost all objects in R^[The exception is the `NULL` type object.] can have attributes, which can be accessed in a variety of ways. Table \@ref(tab:attr) lists the most typical attributes.


Table: (\#tab:attr) Typical attributes and their accessor functions.

| Description     | Attribute name     | Accessor functions   |
|-----------------|--------------------|----------------------|
| Class           | `class`            | `class()`            |
| Column names    | `names`            | `names()`            |
| Dimensions      | `dim`              | `dim()`              |
| Row names       | `row.names`        | `row.names()`        |
| Dimension names | `dimnames`         | `dimnames()`         |
| Commentary      | `comment`          | `comment()`          |

Matrices (and arrays, see below.) are just vectors with a `dim` attribute. Attributes can be obtained using `attributes()` and set in two ways. First, using `attr(x, name)`, where `x` in an object and `name` is, typically, one of the attribute names in table \@ref(tab:attr).

```{r}
aa <- foo1
attributes(aa)
aa
attr(aa, "dim") <- c(3,5)
aa
attributes(aa)

# Remove attributes:
attributes(aa) <- NULL
aa

```

However, `name` can be anything you want, so you are free to make your own attributes.

```{r}
attr(aa, "owner") <- "Rick"
attributes(aa)

attributes(aa)$owner

```

Second, and more typical, is to default to the special accessor functions when they are available.

```{r}

# Use the special accessor functions if available
dim(aa) <- c(3,5)
aa
```

In the case of matrices, we have yet another method, `matrix()`, which gives us more control, e.g. filling row-wise, instead of the default column-wise.

```{r}
attributes(aa) <- NULL
aa
aa <- matrix(aa, nrow = 3, byrow = TRUE)
aa

```


There are a some very convenient^[We typically use them on matrices since every value will be numeric or integer, but they also work on data frames.] functions for working with columns and rows:{#matrices}


```{r}
colMeans(aa)
colSums(aa)

rowMeans(aa)
rowMeans(aa)

```

#### Matrix algebra

If you use the `*` operator to multiply two vectors -- which we can now understand as 1 x n matrices -- R will implement its particular type of vector recycling. If you want to do true matrix algebra, you'll have to use the `\%*\%` operator for inner multiplication or the `\%o\%` operator for outer multiplication.

```{r}

aa <- 1:6

# Inner multiplication
aa %*% aa # i.e. sum(aa * aa)

# Outer multiplication
bb <- c(10, 20)
aa %o% bb
```

### Arrays

![](figures/Arrays.png)

Arrays are _n_-dimensional vectors, which is to say a stack of _n_ matrices.^[Sometimes you'll see arrays referred to as _Multi-dimensional Arrays_.] Just like matrices, they have to be all of the same type. Arrays are useful if you are working on longitudinal data but are not very convenient to work with. You are more likely to arrange your data into a data frame. A useful example is when working with images, the R (red), G (green), and B (blue) channels may be stored in a separate matrix, which together are stored as a multi-dimensional array (MDA). We can create arrays like matrices, changing the dimensions of a vector, or using the `array()` function for more control. Note that when we index arrays, there are three dimensions.

```{r}

aa <- c(11:14, 21:24, 31:34)
dim(aa) = c(2, 2, 3)
aa
```

```{r}
ar <- array(c(11:14, 21:24, 31:34),
            dim = c(2, 2, 3))
ar

```

```{r eval = FALSE, echo = FALSE}
ar[,,1] # The first matrix
ar[,,1][1,] # The first row of the first matrix
# ar[1,,1] The same

ar[,,1][,1] # The first column of the first matrix
# ar[,1,1] The same

ar[1,,] # The first row  of all matrices in the array.
ar[,1,] # The first column of all matrices in the array.

```

## Heterogeneous Data-types

### Lists {#sub:list}

After 1-dimensional vectors, a 1-dimensional list is the most basic type of data structure in R. It's basically a heterogeneous vector -- where each element can be a different type. You may sometimes manually store data in a list, but because they can be a bit cumbersome to work with, you should have a good reason for doing so. For example, since they are a convenient way of storing heterogeneous data, many functions provide their results as a list.^[In some cases, multiple arguments are given in list form.] Actually, we've seen exactly that scenario already.

![](figures/Lists.png)

```{r}
Plant_lm <- lm( weight ~ group, data=PlantGrowth)
typeof(Plant_lm)
```

`Plant_._lm` is a list, and it has two attributes:

```{r}
attributes(Plant_lm)
```

Which should be accessed with the appropriate accessor functions

```{r}
names(Plant_lm)
class(Plant_lm)
```

Remember, anything that is named can be accessed via the $ notation.

```{r}
Plant.lm$coefficients
```

We can see a similar phenomenon with the ANOVA table, stored in `Plant_anova`.

```{r}

Plant_anova <- anova(Plant_lm)
typeof(Plant_anova)

# here there are four attributes:
attributes(Plant_anova)

class(Plant_anova)
```


Here the two objects are both `list` types, but their classes are different.^[Notice also that an object can have more than one class associated with it.] A class is simply an attribute that tells R what to do with this object. For example we can see that when we call print

```{r}
print(Plant_lm) # identical to calling Plant_lm
summary(Plant_lm)

print(Plant_anova) # identical to calling Plant_anova
summary(Plant_anova)

```

^[You'll see that the output for `summary(Plant.anova)` looks very similar to what we'll get for a data frame, which makes sense, since one of the classes is `data frame`.]This is a major feature of object-oriented programming (OOP) -- functions behave differently given the class of an object.

We can make a list from scratch using `list()`:

```{r}

# create a list with 2 elements
l <- list(a = foo1, b = foo2)
l
```

### Data Frames {#sec:dataframes}

![](figures/Dataframes.png)

A data frame is a special type of list, where every every element is a vector of the same length. This means that data frames are two-dimensional tables, like you find in Excel.

**Variables** are vectors stored in vertical columns. Each variable can be it's own data type (see table \@ref(tab:data-modes)).

**Observations** are stored in horizontal rows.

This simple description leaves much room for interpretation. _How_ you organize information in a data frame is important since it can influence how easily you can carry out downstream calculations. A very popular way to organize data frames is called _tidy data_, whereby data frames have:

1. One observation per row,
2. One variable per column, and are composed of
3. One observational unit.

We will discus the best methods for formatting data in the section on _tidy data_.

Regardless of how you organize information in a data frame, there are two note-worthy aspects of data frames:

1. Each column must be the same length.
2. All data within a column must be of the same type.

This makes a lot of sense, since a data frame is just a list of vectors. You can see this in action by using `tibble()` to make a data frame (tibble variant) from the vectors `foo2`, `foo3` and `foo4`.

```{r}
foo_df <- tibble(foo4,foo3,foo2)
foo_df

typeof(foo_df)
```

The following table lists some of the most commonly used functions for working with data frames:

Table: (\#tab:data-table-functions) Frequently used functions for working with data frames.

| Function       | Description                                                                                                     |
|----------------|-----------------------------------------------------------------------------------------------------------------|
| `data.frame()` | Create a data frame.                                                                                            |
| `tibble()`     | From the `{tibble}` package. Create a tibble variant of a data frame                                            |
| `str()`        | Structure of a data frame.                                                                                      |
| `glimpse()`    | From the `{dplyr}` package. Structure of a data frame in a nicer format                                          |
| `summary()`    | Calculate summary statistics on each variable depending on its type                                             |
| `dim()`        | An accessor function for the Dimensions (number of rows and columns).                                           |
| `names()`      | An accessor function for the (column) names attribute.                                                          |
| `row.names()`  | An accessor function for the row names.                                                                         |
| `nrow()`       | Number of rows                                                                                                  |
| `ncol()`       | Number of columns                                                                                               |
| `cbind()`      | Add two data frames together by columns                                                                         |
| `rbind()`      | Add two data frames together by rows                                                                            |
| `subset()`     | Extract  a subset of records, outdates but still in popular use. See the chapter on indexing and the `filter()` |
| `head()`       | Show the head (first x number of rows) of the data frame                                                        | 
| `tail()`       | Show the tail (last x number of rows) of the data frame                                                         |

Let's consider the `foo_df` data frame further. There are 3 attributes associated with this data frame (see table \@ref(tab:attr)).

```{r}
attributes(foo_df)
```

Which we can access by these common accessor functions:

```{r}
# What are the column names?
names(foo_df)

# What are the row names?
row.names(foo_df)

# The class
class(foo_df)
```

There are 6 rows (observations) and 3 columns (variables).

Take special note of the following unique featues of working with data frames:

1. Variable names of a data frame (i.e. column headers) are themselves a character vector.
2. To access specific columns by name, use `$` notation to specify the name of the column, e.g. `\$healthy`. We already saw this with the `PlantGrowth` data frame.
3. To select only those rows of interest, use the `subset()` function and specify the data frame and the logical test of interest corresponding to specific criteria. We'll get into more detail on what that all means in the next section. For now, just be aware that although you will see `subset()` used, it has largely been supplanted by `filter()` from the `{dplyr}` package.   

Since the column names are just a character vector stored as an attribute, we can modify them independently of the data in the data frame.

```{r}
names(foo_df)
# Reassign column names.
names(foo_df) <- myNames
names(foo_df)
```

```{r}
# Select a single column by name.
foo_df$healthy
```

```{r}
# How many rows and columns does foo_df have?
dim(foo_df)
```

It's always useful to run these two functions on a data frame:

```{r}
# str(foo_df) # base package, or
glimpse(foo_df) # Tidyverse, and
summary(foo_df)
```

#### stringsAsFactors {#sub:sAFactors}

R version 4.0, released (April, 2020), changed the default value of the `data.frame()` argument `stringsAsFactors` to `FALSE`. Thus, if you have run this code on an older version of R what you actually see is this:

```{r}
pre4_foo_df <- data.frame(foo4,foo3,foo2, stringsAsFactors = TRUE)
names(pre4_foo_df) <- myNames
glimpse(pre4_foo_df)
```

We'll discuss factors in \@ref(cha:Factors). For what we'll be doing, it does not make a difference.

## Merging Data Frames {#sec:merge}

Merging two data frames according to a common variable is a typical command in data analysis. There are many varieties of merge and redundancy between functions. This guide presents functions in four packages:

- `base` package functions will likely meet all your regular needs
- `dplyr` -- If you're using a `dplyr` workflow, see \@ref(cha:elementSAC), default to these functions^[`plyr` is outdated, but you may still find it used from time to time. It has some handy functions for working with data structures other than data frames. However, avoid mixing `plyr` and `dplyr` in a single R environment, loading both packages can cause conflicts. It's better to use `::` notation to call specific functions. `data.table` is a more advanced data handling package, which is out of the scope of this workshop. Let these functions serve as a preview.].

Beginning with two data frames:

![](figures/Merge_Set-up.png)

```{r}
df1 <- data.frame(Protein = c("CATB", "PSB1", "STUB1", "HUWE1"),
                  Value = c(2.0, 0.1, 0.5, 0.3))
df2 <- data.frame(Protein = c("UBE4b", "SEP15", "CATB", "PSB1"),
                  GO = c(rep("Protein Folding", 2), rep("Proteolysis", 2)))

```


Use merge functions (and optional parameters) from either `base` package or `dplyr` to complete the following exercises.


### Inner join

![](figures/Merge_Inner_Join.png)




```{exercise, name="Inner join"}

Return only the rows in which the left table has matching keys in the right table.
```



```{r}
# Inner join:
merge(df1, df2)

library(dplyr)

#inner
inner_join(df1, df2) # return all rows from x where there are matching values in y, and all columns from x and y

```


R automatically joins the frames by common variable names. To specify only the variables of interest, use `merge(df1, df2, by = "Protein")`. Additionally, use the `by.x` and `by.y` parameters if the matching variables have different names in the two data frames.




### Outer join

![](figures/Merge_Outer_Join.png)


```{exercise, name="Outer join"}
Return all rows from both tables, join records from the left which have matching keys in the right table.
```



```{r}
# Outer join:
merge(x = df1, y = df2, all = TRUE)

# join(df1, df2, type = "full") # return all rows from x, and all columns from x and y
full_join(df1, df2)

```


### Left join

![](figures/Merge_Left_Outer.png)

```{exercise, name="Left join"}
Return all rows from the left table, and any rows with matching keys from the right table.
```



```{r}
# Left outer:
merge(x = df1, y = df2, by = "Protein", all.x = TRUE)

#left outer
left_join(df1, df2) # return all rows from x, and all columns from x and y

```


### Right join

<!-- \includegraphics[scale=0.80]{./figure/Merge_Right_Outer} -->
![](figures/Merge_Right_Outer.png)




```{exercise, name="Right join"}
Return all rows from the right table, and any rows with matching keys from the left table.
```



```{r}
# Right outer:
merge(x = df1, y = df2, by = "Protein", all.y = TRUE)

# #right outer (just reverse argument order)
# left_join(df2, df1)

#right join
right_join(df1, df2) # return all rows from x, and all columns from x and y

```

### Anti-join & Semi-join


![](figures/Merge_Semi_Anti.png)



```{exercise, name="Semi-join"}
Return all the rows from df1, where there \emph{are} matching values in df2, keeping just columns from df1.
```

```{r}
library(dplyr)
semi_join(df1, df2)

```

```{exercise, name="Anti-join"}
Return all the rows from df1 where there \emph{are no} matching values in df2, keeping just columns from df1.
```

```{r}
anti_join(df1, df2)
```


## Ordering functions {#ordering}

There are a couple different ways to think about sorting data. Don't confuse the following functions:

Table: (\#tab:arrange) Examples of some simple and frequently used functions for reordering data.

| Function         | Description                                                                          |
|------------------|--------------------------------------------------------------------------------------|
| `arrange()`      | Part of the `tidyverse`. Rearranges a data frame according to a variable.            |
| `sort()`         | Returns a sorted vector (ascending or descending). Calls `order()` under-the-hood.   |
| `order()`        | Returns an index (integer vector) of the position of the ordered values (ascending or descending). Use this for data frames. Allows ordering on multiple vectors. |
| `rank()`         | Returns the ranks of values in a vector, e.g. in non-parametric tests.                      |

```{r}
foo_df

foo_df %>% 
  arrange(tissue)

foo_df %>% 
  arrange(desc(tissue))
```

```{r}
# A vector (or a variable in a data frame):
foo3
sort(foo3) # values after ordering
order(foo3) # index position after ordering
foo3[order(foo3)] # i.e. sort()
```


A cross join results in all combinations of all variables.

```{r}
merge(x = df1, y = df2, by = NULL)
```
