Data frames

Author

Søren O’Neill & Steen Harsted

Published

February 8, 2024


1 Data frames and tibbles

Data frames are rectangular data structures where each column contains only data of the same data type (but different columns can contain different data types) – in other words, each column is typically a vector1.

my_dataframe <- data.frame(A=1:5, B=letters[1:5])
my_dataframe
  A B
1 1 a
2 2 b
3 3 c
4 4 d
5 5 e
str(my_dataframe)
'data.frame':   5 obs. of  2 variables:
 $ A: int  1 2 3 4 5
 $ B: chr  "a" "b" "c" "d" ...

You will notice from the str() function call, that my_dataframe is a data frame with 2 variables (columns) and 5 observations (rows) … and that those two variables are called A and B and are of types int and chr, respectively. In other words, column 2 which is called B is a vector of characters.

Let us say for instance, that we wanted to add information to the data frame about two different groups X and Y and we wanted each observation distributed to the groups evenly – group set to X for rows 1, 3, 5 and Y for rows 2 and 4. You might think we could rely on R to re-cycle the information the same way it did in the preceding example with multiplication of vectors:

library(tidyverse)
my_dataframe |> mutate(GROUP = c("X", "Y"))

Error in `mutate()`:
ℹ In argument: `GROUP = c("X", "Y")`.
Caused by error:
! `GROUP` must be size 5 or 1, not 2.

This does not work – look at the error message.

The reason is, that when working with data frames (tibbles), we have to make sure we create (mutate) a new column with the correct number of rows. A data frame is defined as a rectangular data structure – each column must have the same number of rows, and vice versa. And each column must be of the same data type.

We could achieve the desired result in a number of ways, e.g. using the repeat to length function rep_len():

my_dataframe |> 
  mutate(GROUP = rep_len(c("X", "Y"), length.out=nrow(my_dataframe)))
  A B GROUP
1 1 a     X
2 2 b     Y
3 3 c     X
4 4 d     Y
5 5 e     X

Now, look at this simple dataframe:

data.frame(a=1:5, b=1:5*2)
  a  b
1 1  2
2 2  4
3 3  6
4 4  8
5 5 10

The function t() transposes the dataframe: It ‘flips’ columns into rows and vice versa:

data.frame(a=1:5, b=1:5*2) |> t()
  [,1] [,2] [,3] [,4] [,5]
a    1    2    3    4    5
b    2    4    6    8   10

…notice the row and column names.

Contemplate what happens when you tranpose this dataframe:

data.frame(a=1:5, b=letters[1:5*2])
  a b
1 1 b
2 2 d
3 3 f
4 4 h
5 5 j
data.frame(a=1:5, b=letters[1:5*2]) |> t()
  [,1] [,2] [,3] [,4] [,5]
a "1"  "2"  "3"  "4"  "5" 
b "b"  "d"  "f"  "h"  "j" 

..remember that each column must be of the same data type, therefor R will coerce the values in row ‘a’ from integers into strings – otherwise each column would contain rows with different data types.

2 Tidyverse tibbles

The Tidyverse meta-package introduces the concept of a tibble:

The colloquial term “tibble” refers to a data frame that has the tbl_df class. Tibble is the central data structure for the set of packages known as the tidyverse, including dplyr, ggplot2, tidyr, and readr. The general ethos is that tibbles are lazy and surly: they do less and complain more than base dataframes.

In most instances, you can regard data frames and tibbles as the same thing…

3 Datasæt og tibbles

Datasæt (data frames) er rektangulære datastrukturer, hvor hver kolonne kun indeholder data af samme datatype (men forskellige kolonner kan indeholde forskellige datatyper) – med andre ord er hver kolonne typisk en vektor2.

my_dataframe <- data.frame(A=1:5, B=letters[1:5])
my_dataframe
  A B
1 1 a
2 2 b
3 3 c
4 4 d
5 5 e
str(my_dataframe)
'data.frame':   5 obs. of  2 variables:
 $ A: int  1 2 3 4 5
 $ B: chr  "a" "b" "c" "d" ...

Du vil bemærke fra kaldet til str()-funktionen, at my_dataframe er et datasæt med 2 variabler (kolonner) og 5 observationer (rækker) … og at de to variabler hedder A og B og er af typerne int og chr, henholdsvis. Med andre ord er kolonne 2, som hedder B, en vektor af karakterer.

Lad os sige, at vi ønskede at tilføje information til datasættet om to forskellige grupper X og Y, og vi ønskede, at hver observation blev fordelt ligeligt mellem grupperne – gruppe X for rækkerne 1, 3, 5 og gruppe Y for rækkerne 2 og 4. Man kunne tro, at vi kunne få R til at genbruge informationen på samme måde som i det foregående eksempel med multiplikation af vektorer:

library(tidyverse)
my_dataframe |> mutate(GROUP = c("X", "Y"))

Error in `mutate()`:
ℹ In argument: `GROUP = c("X", "Y")`.
Caused by error:
! `GROUP` must be size 5 or 1, not 2.

Dette virker ikke – læs fejlmeddelelsen.

Årsagen er, at når man arbejder med datasæt (tibbles), skal man sikre, at man opretter (muterer) en ny kolonne med det korrekte antal rækker. Et datasæt er defineret som en rektangulær datastruktur – hver kolonne skal have det samme antal rækker, og omvendt. Og hver kolonne skal have samme datatype.

Vi kunne opnå det ønskede resultat på flere måder, fx ved at bruge repeat to length-funktionen rep_len():

my_dataframe |> 
  mutate(GROUP = rep_len(c("X", "Y"), length.out=nrow(my_dataframe)))
  A B GROUP
1 1 a     X
2 2 b     Y
3 3 c     X
4 4 d     Y
5 5 e     X

Se nu på dette simple datasæt:

data.frame(a=1:5, b=1:5*2)
  a  b
1 1  2
2 2  4
3 3  6
4 4  8
5 5 10

Funktionen t() transponerer datasættet: Den ‘vender’ kolonner til rækker og omvendt:

data.frame(a=1:5, b=1:5*2) |> t()
  [,1] [,2] [,3] [,4] [,5]
a    1    2    3    4    5
b    2    4    6    8   10

…bemærk rækkenavne og kolonnenavne.

Overvej, hvad der sker, når du transponerer dette datasæt:

data.frame(a=1:5, b=letters[1:5*2])
  a b
1 1 b
2 2 d
3 3 f
4 4 h
5 5 j
data.frame(a=1:5, b=letters[1:5*2]) |> t()
  [,1] [,2] [,3] [,4] [,5]
a "1"  "2"  "3"  "4"  "5" 
b "b"  "d"  "f"  "h"  "j" 

…husk, at hver kolonne skal have samme datatype, derfor vil R tvinge værdierne i rækken ‘a’ fra heltal til tekst – ellers ville hver kolonne indeholde rækker med forskellige datatyper.

4 Tidyverse tibbles

Tidyverse meta-pakken introducerer konceptet tibble:

Det uformelle udtryk “tibble” refererer til et datasæt, der har klassen tbl_df. Tibble er den centrale datastruktur for pakken i tidyverse, inklusive dplyr, ggplot2, tidyr og readr. Den generelle filosofi er, at tibbles er dovne og besværlige: de gør mindre og klager mere end base-datasæt.

I de fleste tilfælde kan du betragte datasæt og tibbles som det samme…

Footnotes

  1. They can also be lists↩︎

  2. De kan også være list↩︎