Manipulating Data Frames

Accessing Data Frame Elements

Because data frames are just lists, we can access a data frame’s columns using the same methods we would use for lists. To access a single column by name, the most efficient method is typically the $ operator.

d1 <- data.frame(
  a = sample(c(TRUE, FALSE), 10, replace = TRUE),
  b = sample(c("foo", "bar"), 10, replace = TRUE),
  c = runif(10)
)

d1
       a   b          c
1  FALSE bar 0.05953239
2   TRUE bar 0.89668374
3   TRUE foo 0.19611331
4  FALSE foo 0.36490166
5  FALSE foo 0.09832523
6  FALSE bar 0.90508043
7  FALSE foo 0.92559116
8  FALSE foo 0.51965613
9  FALSE foo 0.98685013
10  TRUE bar 0.39354245
d1$b
 [1] "bar" "bar" "foo" "foo" "foo" "bar" "foo" "foo" "foo" "bar"

We can also use the single, [], or double, [[]], square bracket operators. As with lists, these operators differ in two respects: how many columns they can select and how they format the resulting selection.

  • [] Can select multiple columns and always returns a data frame.
  • [[]] Can select only one column and returns the column contents as a vector (or whatever type of object the column contained).
# Return a one-column data frame comprising the 'b' column from d1
d1["b"]
     b
1  bar
2  bar
3  foo
4  foo
5  foo
6  bar
7  foo
8  foo
9  foo
10 bar
# The same as above, but using the column index instead of the column name
d1[2]
     b
1  bar
2  bar
3  foo
4  foo
5  foo
6  bar
7  foo
8  foo
9  foo
10 bar
# Return a two-column data frame comprising the 'a' and 'b' columns from d1
d1[c("a", "b")]
       a   b
1  FALSE bar
2   TRUE bar
3   TRUE foo
4  FALSE foo
5  FALSE foo
6  FALSE bar
7  FALSE foo
8  FALSE foo
9  FALSE foo
10  TRUE bar
# The same as above, but using the column indices instead of the column names
d1[1:2]
       a   b
1  FALSE bar
2   TRUE bar
3   TRUE foo
4  FALSE foo
5  FALSE foo
6  FALSE bar
7  FALSE foo
8  FALSE foo
9  FALSE foo
10  TRUE bar
# Return the 'b' column from d1 as a character vector
d1[["b"]]
 [1] "bar" "bar" "foo" "foo" "foo" "bar" "foo" "foo" "foo" "bar"
# The same as above, but using the column index instead of the column name
d1[[2]]
 [1] "bar" "bar" "foo" "foo" "foo" "bar" "foo" "foo" "foo" "bar"

Matrix-Style Selection

Data frames also support matrix-style subsetting, where we define the selection by specifying both the row and column indices.

# Return the first two rows of columns `b` and `c`
d1[1:2, 2:3]
    b          c
1 bar 0.05953239
2 bar 0.89668374
# Return all rows from columns `a` and `b`
d1[, 1:2]
       a   b
1  FALSE bar
2   TRUE bar
3   TRUE foo
4  FALSE foo
5  FALSE foo
6  FALSE bar
7  FALSE foo
8  FALSE foo
9  FALSE foo
10  TRUE bar
# Return the second and third rows of all columns
d1[2:3, ]
     a   b         c
2 TRUE bar 0.8966837
3 TRUE foo 0.1961133

Quirks of Matrix-Style Subsetting

In most cases, matrix style subsetting behaves the same was as the [] list-style operator. You can select any number of columns, and the selection is returned as a data frame, but there is one important exception. If you select a single column using matrix-style subsetting, the selection will be converted to a vector.

d1[, 1]
 [1] FALSE  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE
Going Further

If you want to create a one-column data frame by extracting a single column from another data frame, you should probably use the [] list-style subsetting operator. That being said, if you really want to use matrix-style subsetting, you can achieve the desired effect by adding the drop = FALSE argument inside the brackets.

d1[, 1, drop = FALSE]
       a
1  FALSE
2   TRUE
3   TRUE
4  FALSE
5  FALSE
6  FALSE
7  FALSE
8  FALSE
9  FALSE
10  TRUE

I wouldn’t recommend using this pattern since it produces non-idiomatic R code (i.e., by convention, we should only specify function arguments inside the parentheses of a function call). You might come across this syntax in the wild, however, so it’s worth understanding what’s happening.

Convenience Functions

Calling the head() function on a data frame returns the first n rows of the data frame.

head(d1, 3)
      a   b          c
1 FALSE bar 0.05953239
2  TRUE bar 0.89668374
3  TRUE foo 0.19611331

Likewise, the tail() function returns the final n rows of the data frame.

tail(d1, 3)
       a   b         c
8  FALSE foo 0.5196561
9  FALSE foo 0.9868501
10  TRUE bar 0.3935425

As with vectors, providing a negative value for the n argument returns the complementary number of rows from either the top or bottom of the data frame.

head(d1, -3)
      a   b          c
1 FALSE bar 0.05953239
2  TRUE bar 0.89668374
3  TRUE foo 0.19611331
4 FALSE foo 0.36490166
5 FALSE foo 0.09832523
6 FALSE bar 0.90508043
7 FALSE foo 0.92559116
tail(d1, -3)
       a   b          c
4  FALSE foo 0.36490166
5  FALSE foo 0.09832523
6  FALSE bar 0.90508043
7  FALSE foo 0.92559116
8  FALSE foo 0.51965613
9  FALSE foo 0.98685013
10  TRUE bar 0.39354245
Practice

The iris dataset is provided by Base R and available in any R session.

head(iris, 10)
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1           5.1         3.5          1.4         0.2  setosa
2           4.9         3.0          1.4         0.2  setosa
3           4.7         3.2          1.3         0.2  setosa
4           4.6         3.1          1.5         0.2  setosa
5           5.0         3.6          1.4         0.2  setosa
6           5.4         3.9          1.7         0.4  setosa
7           4.6         3.4          1.4         0.3  setosa
8           5.0         3.4          1.5         0.2  setosa
9           4.4         2.9          1.4         0.2  setosa
10          4.9         3.1          1.5         0.1  setosa

Use the iris dataset to complete the following tasks.

  1. Use three different methods to extract the Petal.Length column.
    • Return the results as vectors.
  2. Extract the Petal.Width column.
    • Return the result as a one-column data frame.
  3. Extract the first 20 rows of the Sepal.Length and Sepal.Width columns.
    • Return the result as a \(20 \times 2\) data frame.
  4. Extract the final 10 rows of the data frame.
    • Return the result as a \(10 \times 5\) data frame.

Q1

iris$Petal.Length
  [1] 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 1.5 1.6 1.4 1.1 1.2 1.5 1.3 1.4
 [19] 1.7 1.5 1.7 1.5 1.0 1.7 1.9 1.6 1.6 1.5 1.4 1.6 1.6 1.5 1.5 1.4 1.5 1.2
 [37] 1.3 1.4 1.3 1.5 1.3 1.3 1.3 1.6 1.9 1.4 1.6 1.4 1.5 1.4 4.7 4.5 4.9 4.0
 [55] 4.6 4.5 4.7 3.3 4.6 3.9 3.5 4.2 4.0 4.7 3.6 4.4 4.5 4.1 4.5 3.9 4.8 4.0
 [73] 4.9 4.7 4.3 4.4 4.8 5.0 4.5 3.5 3.8 3.7 3.9 5.1 4.5 4.5 4.7 4.4 4.1 4.0
 [91] 4.4 4.6 4.0 3.3 4.2 4.2 4.2 4.3 3.0 4.1 6.0 5.1 5.9 5.6 5.8 6.6 4.5 6.3
[109] 5.8 6.1 5.1 5.3 5.5 5.0 5.1 5.3 5.5 6.7 6.9 5.0 5.7 4.9 6.7 4.9 5.7 6.0
[127] 4.8 4.9 5.6 5.8 6.1 6.4 5.6 5.1 5.6 6.1 5.6 5.5 4.8 5.4 5.6 5.1 5.1 5.9
[145] 5.7 5.2 5.0 5.2 5.4 5.1
iris[["Petal.Length"]]
  [1] 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 1.5 1.6 1.4 1.1 1.2 1.5 1.3 1.4
 [19] 1.7 1.5 1.7 1.5 1.0 1.7 1.9 1.6 1.6 1.5 1.4 1.6 1.6 1.5 1.5 1.4 1.5 1.2
 [37] 1.3 1.4 1.3 1.5 1.3 1.3 1.3 1.6 1.9 1.4 1.6 1.4 1.5 1.4 4.7 4.5 4.9 4.0
 [55] 4.6 4.5 4.7 3.3 4.6 3.9 3.5 4.2 4.0 4.7 3.6 4.4 4.5 4.1 4.5 3.9 4.8 4.0
 [73] 4.9 4.7 4.3 4.4 4.8 5.0 4.5 3.5 3.8 3.7 3.9 5.1 4.5 4.5 4.7 4.4 4.1 4.0
 [91] 4.4 4.6 4.0 3.3 4.2 4.2 4.2 4.3 3.0 4.1 6.0 5.1 5.9 5.6 5.8 6.6 4.5 6.3
[109] 5.8 6.1 5.1 5.3 5.5 5.0 5.1 5.3 5.5 6.7 6.9 5.0 5.7 4.9 6.7 4.9 5.7 6.0
[127] 4.8 4.9 5.6 5.8 6.1 6.4 5.6 5.1 5.6 6.1 5.6 5.5 4.8 5.4 5.6 5.1 5.1 5.9
[145] 5.7 5.2 5.0 5.2 5.4 5.1
iris[, "Petal.Length"]
  [1] 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 1.5 1.6 1.4 1.1 1.2 1.5 1.3 1.4
 [19] 1.7 1.5 1.7 1.5 1.0 1.7 1.9 1.6 1.6 1.5 1.4 1.6 1.6 1.5 1.5 1.4 1.5 1.2
 [37] 1.3 1.4 1.3 1.5 1.3 1.3 1.3 1.6 1.9 1.4 1.6 1.4 1.5 1.4 4.7 4.5 4.9 4.0
 [55] 4.6 4.5 4.7 3.3 4.6 3.9 3.5 4.2 4.0 4.7 3.6 4.4 4.5 4.1 4.5 3.9 4.8 4.0
 [73] 4.9 4.7 4.3 4.4 4.8 5.0 4.5 3.5 3.8 3.7 3.9 5.1 4.5 4.5 4.7 4.4 4.1 4.0
 [91] 4.4 4.6 4.0 3.3 4.2 4.2 4.2 4.3 3.0 4.1 6.0 5.1 5.9 5.6 5.8 6.6 4.5 6.3
[109] 5.8 6.1 5.1 5.3 5.5 5.0 5.1 5.3 5.5 6.7 6.9 5.0 5.7 4.9 6.7 4.9 5.7 6.0
[127] 4.8 4.9 5.6 5.8 6.1 6.4 5.6 5.1 5.6 6.1 5.6 5.5 4.8 5.4 5.6 5.1 5.1 5.9
[145] 5.7 5.2 5.0 5.2 5.4 5.1

Q2

iris["Petal.Width"]
    Petal.Width
1           0.2
2           0.2
3           0.2
4           0.2
5           0.2
6           0.4
7           0.3
8           0.2
9           0.2
10          0.1
11          0.2
12          0.2
13          0.1
14          0.1
15          0.2
16          0.4
17          0.4
18          0.3
19          0.3
20          0.3
21          0.2
22          0.4
23          0.2
24          0.5
25          0.2
26          0.2
27          0.4
28          0.2
29          0.2
30          0.2
31          0.2
32          0.4
33          0.1
34          0.2
35          0.2
36          0.2
37          0.2
38          0.1
39          0.2
40          0.2
41          0.3
42          0.3
43          0.2
44          0.6
45          0.4
46          0.3
47          0.2
48          0.2
49          0.2
50          0.2
51          1.4
52          1.5
53          1.5
54          1.3
55          1.5
56          1.3
57          1.6
58          1.0
59          1.3
60          1.4
61          1.0
62          1.5
63          1.0
64          1.4
65          1.3
66          1.4
67          1.5
68          1.0
69          1.5
70          1.1
71          1.8
72          1.3
73          1.5
74          1.2
75          1.3
76          1.4
77          1.4
78          1.7
79          1.5
80          1.0
81          1.1
82          1.0
83          1.2
84          1.6
85          1.5
86          1.6
87          1.5
88          1.3
89          1.3
90          1.3
91          1.2
92          1.4
93          1.2
94          1.0
95          1.3
96          1.2
97          1.3
98          1.3
99          1.1
100         1.3
101         2.5
102         1.9
103         2.1
104         1.8
105         2.2
106         2.1
107         1.7
108         1.8
109         1.8
110         2.5
111         2.0
112         1.9
113         2.1
114         2.0
115         2.4
116         2.3
117         1.8
118         2.2
119         2.3
120         1.5
121         2.3
122         2.0
123         2.0
124         1.8
125         2.1
126         1.8
127         1.8
128         1.8
129         2.1
130         1.6
131         1.9
132         2.0
133         2.2
134         1.5
135         1.4
136         2.3
137         2.4
138         1.8
139         1.8
140         2.1
141         2.4
142         2.3
143         1.9
144         2.3
145         2.5
146         2.3
147         1.9
148         2.0
149         2.3
150         1.8

Q3

iris[1:20, c("Sepal.Length", "Sepal.Width")]
   Sepal.Length Sepal.Width
1           5.1         3.5
2           4.9         3.0
3           4.7         3.2
4           4.6         3.1
5           5.0         3.6
6           5.4         3.9
7           4.6         3.4
8           5.0         3.4
9           4.4         2.9
10          4.9         3.1
11          5.4         3.7
12          4.8         3.4
13          4.8         3.0
14          4.3         3.0
15          5.8         4.0
16          5.7         4.4
17          5.4         3.9
18          5.1         3.5
19          5.7         3.8
20          5.1         3.8

Q4

tail(iris, 10)
    Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
141          6.7         3.1          5.6         2.4 virginica
142          6.9         3.1          5.1         2.3 virginica
143          5.8         2.7          5.1         1.9 virginica
144          6.8         3.2          5.9         2.3 virginica
145          6.7         3.3          5.7         2.5 virginica
146          6.7         3.0          5.2         2.3 virginica
147          6.3         2.5          5.0         1.9 virginica
148          6.5         3.0          5.2         2.0 virginica
149          6.2         3.4          5.4         2.3 virginica
150          5.9         3.0          5.1         1.8 virginica

Modifying Data Frame Elements

At this point, I hope it comes as no surprise to hear that we can overwrite the columns of a data frame using the same procedures that we use to modify list slots. When modifying one column at a time, we directly apply the intuitive operations.

# View the original data frame
d1
       a   b          c
1  FALSE bar 0.05953239
2   TRUE bar 0.89668374
3   TRUE foo 0.19611331
4  FALSE foo 0.36490166
5  FALSE foo 0.09832523
6  FALSE bar 0.90508043
7  FALSE foo 0.92559116
8  FALSE foo 0.51965613
9  FALSE foo 0.98685013
10  TRUE bar 0.39354245
# Modify some columns
d1$a <- LETTERS[1:10]
d1[[2]] <- rnorm(10)
d1["c"] <- rep(c(TRUE, FALSE), each = 5)

# View the modified data frame
d1
   a          b     c
1  A -0.5726434  TRUE
2  B  1.0046224  TRUE
3  C  0.6288373  TRUE
4  D -1.9856082  TRUE
5  E  0.5412699  TRUE
6  F  0.9958062 FALSE
7  G -0.3716671 FALSE
8  H -0.1168243 FALSE
9  I  2.2861396 FALSE
10 J -0.2126098 FALSE

When modifying multiple columns with the [], operator, it’s best to supply the replacement values as a data frame or list with the same size as the selected columns. For example, below we replace the first two columns of d1 with an equivalently sized data frame.

d1[1:2] <- data.frame(1:10, 1:2)
d1
    a b     c
1   1 1  TRUE
2   2 2  TRUE
3   3 1  TRUE
4   4 2  TRUE
5   5 1  TRUE
6   6 2 FALSE
7   7 1 FALSE
8   8 2 FALSE
9   9 1 FALSE
10 10 2 FALSE

Here, we replace the a and c columns in d1 with an equivalently sized list.

d1[c("a", "c")] <- list(rnorm(10), runif(10))
d1
            a b         c
1   0.9187951 1 0.1701443
2  -0.5724222 2 0.6975865
3  -0.3107467 1 0.1793382
4  -1.4230649 2 0.7899628
5   0.2105296 1 0.2084773
6   0.3194327 2 0.8416104
7  -1.2090442 1 0.1326894
8   1.2461163 2 0.2173493
9  -1.0636381 1 0.1631748
10 -1.5323543 2 0.2482311

Matrix-Style Selection

If we only want to replace part of a column, we can use matrix-style selection to choose the target cells. Below, we first overwrite the first five rows in column b. We then overwrite rows 3 through 6 in columns a and c.

d1[1:5, 2] <- 41:45
d1
            a  b         c
1   0.9187951 41 0.1701443
2  -0.5724222 42 0.6975865
3  -0.3107467 43 0.1793382
4  -1.4230649 44 0.7899628
5   0.2105296 45 0.2084773
6   0.3194327  2 0.8416104
7  -1.2090442  1 0.1326894
8   1.2461163  2 0.2173493
9  -1.0636381  1 0.1631748
10 -1.5323543  2 0.2482311
d1[3:6, c("a", "c")] <- list(-99, 888)
d1
             a  b           c
1    0.9187951 41   0.1701443
2   -0.5724222 42   0.6975865
3  -99.0000000 43 888.0000000
4  -99.0000000 44 888.0000000
5  -99.0000000 45 888.0000000
6  -99.0000000  2 888.0000000
7   -1.2090442  1   0.1326894
8    1.2461163  2   0.2173493
9   -1.0636381  1   0.1631748
10  -1.5323543  2   0.2482311

In the second command, notice the pattern in which the contents of the list are recycled to fill the target cells. The recycling rules for data frames can be a little tricky.

Recycling

When the replacement size doesn’t match the selection size, R will use recycling to resolve the discrepancy, as with vectors and matrices. However, it’s not always easy to predict how the replacement will behave.

If we supply a single vector as the replacement data, the recycling operates the same way it does for matrices. For example, in the following code, we replace the first two columns of d1 by recycling the vector 1:5.

d1[1:2] <- 1:5
d1
   a b           c
1  1 1   0.1701443
2  2 2   0.6975865
3  3 3 888.0000000
4  4 4 888.0000000
5  5 5 888.0000000
6  1 1 888.0000000
7  2 2   0.1326894
8  3 3   0.2173493
9  4 4   0.1631748
10 5 5   0.2482311

Nothing new here. We simply re-use the elements of the replacement vector, 1:5, until each selected cell is overwritten. Things get more complicated with we supply the replacement data as a list or data frame, though. Below, we want to replace the a and c columns in d1 with the contents of a list, but the vectors therein aren’t long enough, so they will need to be recycled.

d1[c("a", "c")] <- list(c("yes", "no"), 8:12)
d1
     a b  c
1  yes 1  8
2   no 2  9
3  yes 3 10
4   no 4 11
5  yes 5 12
6   no 1  8
7  yes 2  9
8   no 3 10
9  yes 4 11
10  no 5 12

Note that each list slot acts as the replacement for one of the selected columns. So, the recycling happens within columns: the contents of each list slot are recycled independently to resolve the respective size discrepancy. In this case,

  • The vector c("yes", "no") is recycled five times to match the length of column a.
  • The vector 8:12 only needs to be recycled twice.

Restrictions

As with matrices, R is oddly specific (in a slightly different way) about the kinds of size discrepancies it will automatically resolved when modifying data frames. When all lengths align cleanly (as in the examples above), R will apply recycling silently (i.e., without any messages or warnings).

Ideal

  • All replacement lengths \(\leq\) Respective selection lengths
  • All replacement lengths evenly divide their respective selection lengths
  • Number of replacement list slots = Number of columns selected
  • Number of replacement data frame columns = Number of columns selected

For several other types of size discrepancy, R will apply recycling with a warning.

Acceptable

  • Replacement length > Selection length
  • Replacement length cleanly divides the selection length
  • Replacement length exceeds selection length
  • Replacement list contains more slots than columns selected
  • Replacement data frame contains more slots than columns selected

In one specific situation, R will throw an error and refuse to implement the replacement.

Invalid

  • Replacement length does not evenly divide the selection length.

Examples of Acceptable Replacement

When working with data frames, R is quite permissive with abuses of recycling. So, all of the following examples represent legal uses of replacement that R will run without errors, but these examples will all trigger warnings.

Replace the first two columns of d1 by using the first 20 elements from the vector 100:500.

d1[1:2] <- 100:500
Warning in matrix(value, n, p): data length [401] is not a sub-multiple or
multiple of the number of rows [10]
d1
     a   b  c
1  100 110  8
2  101 111  9
3  102 112 10
4  103 113 11
5  104 114 12
6  105 115  8
7  106 116  9
8  107 117 10
9  108 118 11
10 109 119 12

Replace the a and c columns in d1 with a list containing vectors that are too long.

d1[c("a", "c")] <- list(rnorm(100), runif(100))
Warning in `[<-.data.frame`(`*tmp*`, c("a", "c"), value =
list(c(-0.535388500130966, : replacement element 1 has 100 rows to replace 10
rows
Warning in `[<-.data.frame`(`*tmp*`, c("a", "c"), value =
list(c(-0.535388500130966, : replacement element 2 has 100 rows to replace 10
rows
d1
            a   b          c
1  -0.5353885 110 0.26374842
2  -0.9931941 111 0.57205526
3  -0.6473954 112 0.34406680
4  -1.0534366 113 0.18382629
5   1.1547795 114 0.13603892
6   1.5953771 115 0.95651939
7  -0.2341340 116 0.39505284
8   1.3625139 117 0.84646849
9   0.5434835 118 0.01676838
10  0.5772128 119 0.21642427

Replace the a and c columns in d1 with the first two slots in a length-3 list.

d1[c("a", "c")] <- list(1, 2, 3)
Warning in `[<-.data.frame`(`*tmp*`, c("a", "c"), value = list(1, 2, 3)):
provided 3 variables to replace 2 variables
d1
   a   b c
1  1 110 2
2  1 111 2
3  1 112 2
4  1 113 2
5  1 114 2
6  1 115 2
7  1 116 2
8  1 117 2
9  1 118 2
10 1 119 2

Replace the a and c columns in d1 with the first two columns of a three-column data frame.

d1[c("a", "c")] <- data.frame("foo", "bar", "baz")
Warning in `[<-.data.frame`(`*tmp*`, c("a", "c"), value = structure(list(:
provided 3 variables to replace 2 variables
d1
     a   b   c
1  foo 110 bar
2  foo 111 bar
3  foo 112 bar
4  foo 113 bar
5  foo 114 bar
6  foo 115 bar
7  foo 116 bar
8  foo 117 bar
9  foo 118 bar
10 foo 119 bar

Examples of Invalid Replacement

For the most part, R will only complain when the replacement length does not cleanly divide selection length. So, the following two examples represent some of the few situations in which R will completely refuse to apply our requested replacement.

Here, we try to replace the first two columns of d1 (i.e., 20 total cells) with the length-three vector 1:3.

d1[1:2] <- 1:3
Error in `[<-.data.frame`:
! replacement has 3 items, need 20

This time, we make essentially the same mistake, but now the incompatible vectors live in a list. We’re trying to replace the a and c columns in d1 with a list containing a length-four vector and a length-eight vector.

d1[c("a", "c")] <- list(letters[4], 1:8)
Error in `[<-.data.frame`:
! replacement element 2 has 8 rows, need 10

Adding Columns

As with lists, we can add new columns to an existing data frame using the $ or [[]] operators.

# Create a baseline data frame
(d2 <- data.frame(a = rep(0, 10)))
   a
1  0
2  0
3  0
4  0
5  0
6  0
7  0
8  0
9  0
10 0
# Various ways of adding new single columns
d2$b <- letters[1:10]
d2[["c"]] <- runif(10)
d2[[4]] <- rnorm(10)
d2
   a b          c          V4
1  0 a 0.16675152  0.62950639
2  0 b 0.66826056 -0.84161264
3  0 c 0.06672647  0.61689049
4  0 d 0.06259623  1.89699518
5  0 e 0.19566288 -1.55533153
6  0 f 0.37155647  1.03521774
7  0 g 0.52814765 -0.02203947
8  0 h 0.71506695 -0.04771449
9  0 i 0.16562455 -1.61827805
10 0 j 0.87183256 -0.63305394

As you may expect, R will use recycling when we specify too few elements for the new column.

d2["alice"] <- c(TRUE, FALSE)
d2
   a b          c          V4 alice
1  0 a 0.16675152  0.62950639  TRUE
2  0 b 0.66826056 -0.84161264 FALSE
3  0 c 0.06672647  0.61689049  TRUE
4  0 d 0.06259623  1.89699518 FALSE
5  0 e 0.19566288 -1.55533153  TRUE
6  0 f 0.37155647  1.03521774 FALSE
7  0 g 0.52814765 -0.02203947  TRUE
8  0 h 0.71506695 -0.04771449 FALSE
9  0 i 0.16562455 -1.61827805  TRUE
10 0 j 0.87183256 -0.63305394 FALSE

We can add multiple columns using the [] operator.

d2[6:7] <- rnorm(20)
d2[c("foo", "bar")] <- list(TRUE, FALSE)
d2
   a b          c          V4 alice         V6         V7  foo   bar
1  0 a 0.16675152  0.62950639  TRUE -1.1262705  0.9714567 TRUE FALSE
2  0 b 0.66826056 -0.84161264 FALSE  0.4890620  0.1815502 TRUE FALSE
3  0 c 0.06672647  0.61689049  TRUE -0.9121183 -1.4094829 TRUE FALSE
4  0 d 0.06259623  1.89699518 FALSE -0.3190297 -0.5837219 TRUE FALSE
5  0 e 0.19566288 -1.55533153  TRUE -1.4516665  0.7839286 TRUE FALSE
6  0 f 0.37155647  1.03521774 FALSE -1.5835851 -0.6618996 TRUE FALSE
7  0 g 0.52814765 -0.02203947  TRUE -1.6295050 -0.7508775 TRUE FALSE
8  0 h 0.71506695 -0.04771449 FALSE -0.5745959 -0.1194467 TRUE FALSE
9  0 i 0.16562455 -1.61827805  TRUE  0.5779600  0.7589953 TRUE FALSE
10 0 j 0.87183256 -0.63305394 FALSE -0.7166323 -0.5986976 TRUE FALSE
Practice

Run the following code to create an empty data frame containing 10 observations of the 3 variables: a,b, c. Then populate the data frame as described below.

  1. Fill column a with the integer sequence from -9 to 0.
    • Use the column name to assign the new values.
  2. Fill column b with the even integers between 1 and 20 (inclusive).
    • Use the numeric column index to assign the new values.
  3. Replace the odd rows in column c with the odd integers between 11 and 20 (inclusive).
    • Do not overwrite the missing values in the even rows.
df <- data.frame(a = rep(NA, 10), b = rep(NA, 10), c = rep(NA, 10))
df
    a  b  c
1  NA NA NA
2  NA NA NA
3  NA NA NA
4  NA NA NA
5  NA NA NA
6  NA NA NA
7  NA NA NA
8  NA NA NA
9  NA NA NA
10 NA NA NA
df$a <- -9:0
df[2] <- seq(2, 20, 2)
df[seq(1, 9, 2), "c"] <- seq(11, 19, 2)
df
    a  b  c
1  -9  2 11
2  -8  4 NA
3  -7  6 13
4  -6  8 NA
5  -5 10 15
6  -4 12 NA
7  -3 14 17
8  -2 16 NA
9  -1 18 19
10  0 20 NA
Knowledge Check
  • If d1 is a data frame, why does d1[2] return a one-column data frame, while d1[[2]] returns a vector?
  • Suppose you try to replace two columns in a data frame with the contents of a three-slot list.
    • Which list slots (if any) does R use to fill the columns?
    • Will R raise an error?
    • Will R show a warning?
Back to top