Exploring Data Frames

Огляд

Викладання: 20 хв
Вправи: 10 хв
Питання
  • How can I manipulate a data frame?

Цілі
  • Remove rows with NA values.

  • Append two data frames.

  • Understand what a factor is.

  • Convert a factor to a character vector and vice versa.

  • Display basic properties of data frames including size and class of the columns, names, and first few rows.

На цьому етапі, ви бачили все: на минулому уроці, ми ознайомились з усіма основними типами даних і структурами даних в R. Все, що ви робите, буде маніпуляцією з цими інструментами. Але найчастіше, зіркою шоу є фрейм даних — таблиця, яку ми створили шляхом завантаження інформації з файлу csv. У цьому уроці, ми дізнаємося ще кілька речей про роботу з фреймами даних.

Реалістичний приклад

Ми вже дізнались, що стовпці кадру даних є векторами, тому наші дані мають узгоджений тип у всіх стовпцях. Поки що, ви бачили основи маніпулювання кадрами даних за допомогою наших скандинавських даних; тепер давайте використаємо ці навички, щоб переварити більший набір даних. Давайте прочитаємо набір даних gapminder, який ми завантажили раніше :

Різні поради

  • Ще один тип файлів, з яким ви можете зіткнутись, це файли із розділеними табуляціями (.tsv). Щоб указати табуляцію, використовуйте "\\t" або read.delim().

  • Файли також можна завантажувати безпосередньо з Інтернету в локальну папку за вашим вибором на ваш комп’ютер, використовуючи функцію download.file. Потім цю функцію read.csv можна виконати для читання завантаженого файлу з місця завантаження, наприклад,

download.file("https://raw.githubusercontent.com/datacarpentry/r-intro-geospatial/master/_episodes_rmd/data/gapminder_data.csv",
              destfile = "data/gapminder_data.csv")
gapminder <- read.csv("data/gapminder_data.csv")
  • Крім того, ви також можете читати файли безпосередньо в R з інтернету, замінивши шляхи файлів веб-адресою в read.csv. Слід зауважити, що при цьому локальна копія файлу csv спочатку не зберігається на вашому комп’ютері. Наприклад,
gapminder <- read.csv("https://raw.githubusercontent.com/datacarpentry/r-intro-geospatial/master/_episodes_rmd/data/gapminder_data.csv")
  • Ви можете читати безпосередньо з таблиць excel не перетворюючи їх у звичайний текст за допомогою пакета readxl.

Давайте трохи дослідимо дата фрейм gapminder; перше, що ми повинні завжди робити, це перевіряти, як викглядають дані за допомогою str:

str(gapminder)
'data.frame':\t1704 obs. of  6 variables:
 $ country  : chr  "Afghanistan" "Afghanistan" "Afghanistan" "Afghanistan" ...
 $ year     : int  1952 1957 1962 1967 1972 1977 1982 1987 1992 1997 ...
 $ pop      : num  8425333 9240934 10267083 11537966 13079460 ...
 $ continent: chr  "Asia" "Asia" "Asia" "Asia" ...
 $ lifeExp  : num  28.8 30.3 32 34 36.1 ...
 $ gdpPercap: num  779 821 853 836 740 ...

Ми також можемо перевірити окремі стовпці кадру даних за допомогою функції class:

class(gapminder$year)
[1] "integer"
class(gapminder$country)
[1] "character"
str(gapminder$country)
 chr [1:1704] "Afghanistan" "Afghanistan" "Afghanistan" "Afghanistan" ...

Ми також можемо запитати кадр даних для отримання інформації про його розміри; пам’ятаючи те, що str(gapminder) сказав, що в gapminder було 1704 спостережень за 6 змінними, як ви думаєте, що дасть наступне і чому?

length(gapminder)

Справедливим припущенням було б сказати, що довжина кадру даних дорівнює кількості рядків, які він має (1704), але це не так; це дає нам кількість стовпців.

class(gapminder)
[1] "data.frame"

Щоб отримати кількість рядків і стовпців у нашому наборі даних, спробуйте:

nrow(gapminder)
[1] 1704
ncol(gapminder)
[1] 6

Або обидва одразу:

dim(gapminder)
[1] 1704    6

Ймовірно, ми також захочемо знати заголовки всіх стовпців, щоб запитати їх пізніше:

colnames(gapminder)
[1] "country"   "year"      "pop"       "continent" "lifeExp"   "gdpPercap"

На цьому етапі, важливо запитати себе,яи структура про яку повідомляє R відповідає нашій інтуїції чи очікуванням; чи мають сенс основні типи даних, які повідомляють для кожного стовпця? Якщо ні, нам потрібно розібратися з будь-якими проблемами зараз, поки вони не обернуться непприємними сюрпризами, виуористовуючи те, що ми дізналися, про те, як R інтерпретує дані, і важливість суворої узгодженості в тому, як ми записуємо наші дані.

Коли ми переконаємося, що типи та структури виглядають прийнятно, настав час почати копатися в наших даних. Перегляньте кілька перших рядків:

head(gapminder)
      country year      pop continent lifeExp gdpPercap
1 Afghanistan 1952  8425333      Asia  28.801  779.4453
2 Afghanistan 1957  9240934      Asia  30.332  820.8530
3 Afghanistan 1962 10267083      Asia  31.997  853.1007
4 Afghanistan 1967 11537966      Asia  34.020  836.1971
5 Afghanistan 1972 13079460      Asia  36.088  739.9811
6 Afghanistan 1977 14880372      Asia  38.438  786.1134

Завдання 1

Рекомендується також перевіряти кілька останніх рядків даних і деякі посередині. Як би ви це зробили?

Пошук тих, що знаходятьчя саме посередині, не надто складний, але ми можемо просто запитати кілька рядків навмання. Як би ви написали цей код?

Розв’язання до завдання 1

Перевірити останні кілька рядків відносно просто, оскільки R вже має функцію для цього:

tail(gapminder)
tail(gapminder, n = 15)

А як щодо кількох довільних рядків лише для розсудливості (або божевілля залежно від вашої точки зору)?

Є кілька способів досягти цього.

Рішення тут представляє одну форму з використанням фкладених функцій, тобто функція передається як аргумент іншій функції. Це може звучати, як нова концепція, але по факту, ви вже її використовуєте.

Пам’ятайте my_dataframe[rows, cols] надрукує ваш дата фрейм з тією кількістю рядків і стовпців, яку ви попросите (хоча ви могли просити, наприклад, діапазон або іменовані стовпці). Як отримати останній рядок, якщо ви не знаєте, скільки рядків має ваш дата фрейм? R має функцію для цього. Як щодо отримання (псевдовипадкової) вибірки? R такоє має функцію для цього.

gapminder[sample(nrow(gapminder), 5), ]

Завдання 2

Прочитайте вихід str(gapminder) знову; цього разу, використовуйте, те що ви дізнались про множини та вектори, а також про вихід функцій, таких як colnames і dim, щоб пояснити, що означає все, що друкує str з gapminder. Якщо є якісь частини, які ви не можете інтерпретувати, обговоріть зі своїми одногрупниками!

Розв’язання до завдання 2

Об’єкт gapminder - це фрейм даних зі стовпцями

  • country і continent є факторами.
  • year - це цілий вектор.
  • pop, lifeExp, і gdpPercap - це числові вектори.

Adding columns and rows in data frames

We would like to create a new column to hold information on whether the life expectancy is below the world average life expectancy (70.5) or above:

below_average <- gapminder$lifeExp < 70.5
head(gapminder)
      country year      pop continent lifeExp gdpPercap
1 Afghanistan 1952  8425333      Asia  28.801  779.4453
2 Afghanistan 1957  9240934      Asia  30.332  820.8530
3 Afghanistan 1962 10267083      Asia  31.997  853.1007
4 Afghanistan 1967 11537966      Asia  34.020  836.1971
5 Afghanistan 1972 13079460      Asia  36.088  739.9811
6 Afghanistan 1977 14880372      Asia  38.438  786.1134

We can then add this as a column via:

cbind(gapminder, below_average)
      country year      pop continent lifeExp gdpPercap below_average
1 Afghanistan 1952  8425333      Asia  28.801  779.4453          TRUE
2 Afghanistan 1957  9240934      Asia  30.332  820.8530          TRUE
3 Afghanistan 1962 10267083      Asia  31.997  853.1007          TRUE
4 Afghanistan 1967 11537966      Asia  34.020  836.1971          TRUE
5 Afghanistan 1972 13079460      Asia  36.088  739.9811          TRUE
6 Afghanistan 1977 14880372      Asia  38.438  786.1134          TRUE

We probably don’t want to print the entire dataframe each time, so let’s put our cbind command within a call to head to return only the first six lines of the output.

head(cbind(gapminder, below_average))
      country year      pop continent lifeExp gdpPercap below_average
1 Afghanistan 1952  8425333      Asia  28.801  779.4453          TRUE
2 Afghanistan 1957  9240934      Asia  30.332  820.8530          TRUE
3 Afghanistan 1962 10267083      Asia  31.997  853.1007          TRUE
4 Afghanistan 1967 11537966      Asia  34.020  836.1971          TRUE
5 Afghanistan 1972 13079460      Asia  36.088  739.9811          TRUE
6 Afghanistan 1977 14880372      Asia  38.438  786.1134          TRUE

Note that if we tried to add a vector of below_average with a different number of entries than the number of rows in the dataframe, it would fail:

below_average <- c(TRUE, TRUE, TRUE, TRUE, TRUE)
head(cbind(gapminder, below_average))
Error in data.frame(..., check.names = FALSE): arguments imply differing number of rows: 1704, 5

Why didn’t this work? R wants to see one element in our new column for every row in the table:

nrow(gapminder)
[1] 1704
length(below_average)
[1] 5

So for it to work we need either to have nrow(gapminder) = length(below_average) or nrow(gapminder) to be a multiple of length(below_average):

below_average <- c(TRUE, TRUE, FALSE)
head(cbind(gapminder, below_average))
      country year      pop continent lifeExp gdpPercap below_average
1 Afghanistan 1952  8425333      Asia  28.801  779.4453          TRUE
2 Afghanistan 1957  9240934      Asia  30.332  820.8530          TRUE
3 Afghanistan 1962 10267083      Asia  31.997  853.1007         FALSE
4 Afghanistan 1967 11537966      Asia  34.020  836.1971          TRUE
5 Afghanistan 1972 13079460      Asia  36.088  739.9811          TRUE
6 Afghanistan 1977 14880372      Asia  38.438  786.1134         FALSE

The sequence TRUE,TRUE,FALSE is repeated over all the gapminder rows.

Let’s overwite the content of gapminder with our new data frame.

below_average <-  as.logical(gapminder$lifeExp<70.5)
gapminder <- cbind(gapminder, below_average)

Now how about adding rows? The rows of a data frame are lists:

new_row <- list('Norway', 2016, 5000000, 'Nordic', 80.3, 49400.0, FALSE)
gapminder_norway <- rbind(gapminder, new_row)
tail(gapminder_norway)
      country year      pop continent lifeExp  gdpPercap below_average
1700 Zimbabwe 1987  9216418    Africa  62.351   706.1573          TRUE
1701 Zimbabwe 1992 10704340    Africa  60.377   693.4208          TRUE
1702 Zimbabwe 1997 11404948    Africa  46.809   792.4500          TRUE
1703 Zimbabwe 2002 11926563    Africa  39.989   672.0386          TRUE
1704 Zimbabwe 2007 12311143    Africa  43.487   469.7093          TRUE
1705   Norway 2016  5000000    Nordic  80.300 49400.0000         FALSE

To understand why R is giving us a warning when we try to add this row, let’s learn a little more about factors.

Фактори

Here is another thing to look out for: in a factor, each different value represents what is called a level. In our case, the factor “continent” has 5 levels: “Africa”, “Americas”, “Asia”, “Europe” and “Oceania”. R will only accept values that match one of the levels. If you add a new value, it will become NA.

The warning is telling us that we unsuccessfully added “Nordic” to our continent factor, but 2016 (a numeric), 5000000 (a numeric), 80.3 (a numeric), 49400.0 (a numeric) and FALSE (a logical) were successfully added to country, year, pop, lifeExp, gdpPercap and below_average respectively, since those variables are not factors. ‘Norway’ was also successfully added since it corresponds to an existing level. To successfully add a gapminder row with a “Nordic” continent, add “Nordic” as a level of the factor:

levels(gapminder$continent)
NULL
levels(gapminder$continent) <- c(levels(gapminder$continent), "Nordic")
gapminder_norway  <- rbind(gapminder,
                           list('Norway', 2016, 5000000, 'Nordic', 80.3,49400.0, FALSE))
Warning in `[<-.factor`(`*tmp*`, ri, value = structure(c("Asia", "Asia", :
invalid factor level, NA generated
tail(gapminder_norway)
      country year      pop continent lifeExp  gdpPercap below_average
1700 Zimbabwe 1987  9216418      <NA>  62.351   706.1573          TRUE
1701 Zimbabwe 1992 10704340      <NA>  60.377   693.4208          TRUE
1702 Zimbabwe 1997 11404948      <NA>  46.809   792.4500          TRUE
1703 Zimbabwe 2002 11926563      <NA>  39.989   672.0386          TRUE
1704 Zimbabwe 2007 12311143      <NA>  43.487   469.7093          TRUE
1705   Norway 2016  5000000    Nordic  80.300 49400.0000         FALSE

Alternatively, we can change a factor into a character vector; we lose the handy categories of the factor, but we can subsequently add any word we want to the column without babysitting the factor levels:

str(gapminder)
'data.frame':\t1704 obs. of  7 variables:
 $ country      : chr  "Afghanistan" "Afghanistan" "Afghanistan" "Afghanistan" ...
 $ year         : int  1952 1957 1962 1967 1972 1977 1982 1987 1992 1997 ...
 $ pop          : num  8425333 9240934 10267083 11537966 13079460 ...
 $ continent    : chr  "Asia" "Asia" "Asia" "Asia" ...
  ..- attr(*, "levels")= chr "Nordic"
 $ lifeExp      : num  28.8 30.3 32 34 36.1 ...
 $ gdpPercap    : num  779 821 853 836 740 ...
 $ below_average: logi  TRUE TRUE TRUE TRUE TRUE TRUE ...
gapminder$continent <- as.character(gapminder$continent)
str(gapminder)
'data.frame':\t1704 obs. of  7 variables:
 $ country      : chr  "Afghanistan" "Afghanistan" "Afghanistan" "Afghanistan" ...
 $ year         : int  1952 1957 1962 1967 1972 1977 1982 1987 1992 1997 ...
 $ pop          : num  8425333 9240934 10267083 11537966 13079460 ...
 $ continent    : chr  "Asia" "Asia" "Asia" "Asia" ...
 $ lifeExp      : num  28.8 30.3 32 34 36.1 ...
 $ gdpPercap    : num  779 821 853 836 740 ...
 $ below_average: logi  TRUE TRUE TRUE TRUE TRUE TRUE ...

Appending to a data frame

The key to remember when adding data to a data frame is that columns are vectors and rows are lists. We can also glue two data frames together with rbind:

gapminder <- rbind(gapminder, gapminder)
tail(gapminder, n=3)
      country year      pop continent lifeExp gdpPercap below_average
3406 Zimbabwe 1997 11404948    Africa  46.809  792.4500          TRUE
3407 Zimbabwe 2002 11926563    Africa  39.989  672.0386          TRUE
3408 Zimbabwe 2007 12311143    Africa  43.487  469.7093          TRUE

But now the row names are unnecessarily complicated (not consecutive numbers). We can remove the rownames, and R will automatically re-name them sequentially:

rownames(gapminder) <- NULL
head(gapminder)
      country year      pop continent lifeExp gdpPercap below_average
1 Afghanistan 1952  8425333      Asia  28.801  779.4453          TRUE
2 Afghanistan 1957  9240934      Asia  30.332  820.8530          TRUE
3 Afghanistan 1962 10267083      Asia  31.997  853.1007          TRUE
4 Afghanistan 1967 11537966      Asia  34.020  836.1971          TRUE
5 Afghanistan 1972 13079460      Asia  36.088  739.9811          TRUE
6 Afghanistan 1977 14880372      Asia  38.438  786.1134          TRUE

Завдання 3

You can create a new data frame right from within R with the following syntax:

df <- data.frame(id = c("a", "b", "c"),
                 x = 1:3,
                 y = c(TRUE, TRUE, FALSE),
                 stringsAsFactors = FALSE)

Make a data frame that holds the following information for yourself:

  • first name
  • last name
  • lucky number

Then use rbind to add an entry for the people sitting beside you. Finally, use cbind to add a column with each person’s answer to the question, “Is it time for coffee break?”

Solution to Challenge 3

df <- data.frame(first = c("Grace"),
                 last = c("Hopper"),
                 lucky_number = c(0),
                 stringsAsFactors = FALSE)
df <- rbind(df, list("Marie", "Curie", 238) )
df <- cbind(df, coffeetime = c(TRUE, TRUE))

Ключові моменти

  • Use cbind() to add a new column to a data frame.

  • Use rbind() to add a new row to a data frame.

  • Remove rows from a data frame.

  • Use na.omit() to remove rows from a data frame with NA values.

  • Use levels() and as.character() to explore and manipulate factors.

  • Use str(), nrow(), ncol(), dim(), colnames(), rownames(), head(), and typeof() to understand the structure of a data frame.

  • Read in a csv file using read.csv().

  • Understand what length() of a data frame represents.