Data frame Manipulation with dplyr

Огляд

Викладання: 30 хв
Вправи: 10 хв
Питання
  • How can I manipulate dataframes without repeating myself?

Цілі
  • To be able to use the six main dataframe manipulation ‘verbs’ with pipes in dplyr.

  • To understand how group_by() and summarize() can be combined to summarize datasets.

  • Be able to analyze a subset of data using logical filtering.

Маніпулювання йафлами даних означає багато речей для багатьох дослідників, ми часто вибираємо певні спостереження (рядки) або змінні (стовпці), ми часто групуємо дані за певними змінними, або навіть обчислюємо сумарну статистику. Ми можемо виконати ці операції використовуючи звичайні базові R операції:

mean(gapminder[gapminder$continent == "Africa", "gdpPercap"])
[1] 2193.755
mean(gapminder[gapminder$continent == "Americas", "gdpPercap"])
[1] 7136.11
mean(gapminder[gapminder$continent == "Asia", "gdpPercap"])
[1] 7902.15

Але це не дуже ефективно, і це може стати швидко виснажливо, тому що є справедливе повторення. Повторюючи своїх дій коштуватиме вам часу, як зараз так і пізніше, і потенційно призведе до деяких неприємних помилок.

Пакет dplyr

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

Тут ми розглянемо 6 найбільш часто використовуваних функцій, а також використання конвеєрів (%>%) для їхнього комбінування.

  1. select()
  2. filter()
  3. group_by()
  4. summarize()
  5. mutate()

Якщо ви не встановили цей пакет раніше, зробіть наступне:

install.packages('dplyr')

Зараз давайте завантажимо цей пакет:

library("dplyr")

Використання select()

Якщо, наприклад, ми хочемо рухатись далі лише з декількома змінними нашого файлу даних, ми можемо використовувати функцію select(). Це збереже тільки вибрані змінні.

year_country_gdp <- select(gapminder, year, country, gdpPercap)

Якщо ми відкриємо year_country_gdp ми побачимо, що він містить тільки рік, країну і рівень ВВП. Вище ми використовували ‘нормальну’ граматику, але сильні сторони dplyr полягають у поєднанні декількох функцій за допомогою конвеєрів. Оскільки граматика конвеєрів не схожа на все, що ми бачили раніше в R, давайте повторимо, те що ми зробили раніше використовуючи конвеєри.

year_country_gdp <- gapminder %>% select(year,country,gdpPercap)

Щоб допомогти вам зрозуміти, чому ми написали це таким чином, давайте пройдемося по ньому крок за кроком. Спочатку ми викликаємо «gapminder» файл даних і передаємо його, використовуючи символ конвеєри %>%, до наступного кроку, який є функцією select(). У цьому випадку ми не вказуємо, який об’єкт даних ми використовуємо в функції select() оскільки він отримує дані з попереднього конвеєру. Веселий факт: Ви можливо стикалися з конвеєрами перед цим у терміналі. В R, символ конвеєра це%>% в той час як в терміналі це |, але концепція така ж!

Використання filter()

Якби ми зараз хотіли рухатися вперед з вищенаведеними, але тільки з країнами Європи ми можемо поєднати select і filter

year_country_gdp_euro <- gapminder %>%
  filter(continent == "Europe") %>%
  select(year, country, gdpPercap)

Завдання 1

Напишіть одну команду (яка може охоплювати кілька рядків і включає конвеєри), що буде виробляти таблицю даних, яка має африканські цінності для lifeExp, country іyear, але не для інших континентів. Скільки рядків у вашому файлі даних і чому?

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

year_country_lifeExp_Africa <- gapminder %>%
                           filter(continent=="Africa") %>%
                           select(year,country,lifeExp)

Як і минулого разу, спочатку ми передаємо дані gapminder до функції filter(), потім ми передаємо відфільтровану версію даних gapminder в функцію select(). Примітка: Порядок операцій дуже важливий в цьому випадку. Якщо ми спочатку використаємо ‘select’, фільтер не зможе знайти змінну материка, оскільки ми прибрали його на попередньому кроці.

Використання group_by() і summarize()

Тепер, ми повинні були зменшити похибку повторюваності того, що зможе бути зроблено з базою R, але до цих пір ми не зробили цього, так як ми повинні були б повторювати вищевказане для кожного континенту. Замість filter(), який тільки пройде спостереження, які відповідають вашим критеріям (in the above: continent=="Europe"), ми можемо використовувати group_by(), який буде по суті використовувати всі унікальні критерії, які ви могли використати у фільтрі.

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 ...
gapminder %>% group_by(continent) %>% str()
tibble [1,704 × 6] (S3: grouped_df/tbl_df/tbl/data.frame)
 $ country  : chr [1:1704] "Afghanistan" "Afghanistan" "Afghanistan" "Afghanistan" ...
 $ year     : int [1:1704] 1952 1957 1962 1967 1972 1977 1982 1987 1992 1997 ...
 $ pop      : num [1:1704] 8425333 9240934 10267083 11537966 13079460 ...
 $ continent: chr [1:1704] "Asia" "Asia" "Asia" "Asia" ...
 $ lifeExp  : num [1:1704] 28.8 30.3 32 34 36.1 ...
 $ gdpPercap: num [1:1704] 779 821 853 836 740 ...
 - attr(*, "groups")= tibble [5 × 2] (S3: tbl_df/tbl/data.frame)
  ..$ continent: chr [1:5] "Africa" "Americas" "Asia" "Europe" ...
  ..$ .rows    : list<int> [1:5] 
  .. ..$ : int [1:624] 25 26 27 28 29 30 31 32 33 34 ...
  .. ..$ : int [1:300] 49 50 51 52 53 54 55 56 57 58 ...
  .. ..$ : int [1:396] 1 2 3 4 5 6 7 8 9 10 ...
  .. ..$ : int [1:360] 13 14 15 16 17 18 19 20 21 22 ...
  .. ..$ : int [1:24] 61 62 63 64 65 66 67 68 69 70 ...
  .. ..@ ptype: int(0) 
  ..- attr(*, ".drop")= logi TRUE

Ви помітите, що структура dataframe, де ми використовували group_by() (grouped_df) не збігається з оригінальним gapminder (data.frame). grouped_df можна розглядати як list, де кожен елемент у list є data.frame який містить лише рядки, що відповідають певному значенню ‘континент’ (принаймні, у наведеному вище прикладі).

Використання summarize()

Вище було трохи на нерівномірній стороні, але group_by() набагато більше захоплююче у поєднанні з summarize(). Це дозволить нам створювати нові змінні за допомогою функцій, які повторюються для кожного конкретного континенту файлу даних. Тобто, використовуючи функцію group_by(), ми розбиваємо нашу оригінальну таблицю даних на кілька частин, потім ми можемо запустити функції (e.g. mean() абоsd()) всередині summarize().

gdp_bycontinents <- gapminder %>%
  group_by(continent) %>%
  summarize(mean_gdpPercap = mean(gdpPercap))
`summarise()` ungrouping output (override with `.groups` argument)
gdp_bycontinents
# A tibble: 5 x 2
  continent mean_gdpPercap
  <chr>              <dbl>
1 Africa             2194.
2 Americas           7136.
3 Asia               7902.
4 Europe            14469.
5 Oceania           18622.

Це дозволило нам розрахувати середній показник ВВП для кожного континенту, але він стає ще краще.

Завдання 2

Розрахуйте середню тривалість життя на країну. Яка має найдовшу середню триваліть життя і яка має найкоротшу середню триваліть життя?

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

lifeExp_bycountry <- gapminder %>%
   group_by(country) %>%
   summarize(mean_lifeExp=mean(lifeExp))
`summarise()` ungrouping output (override with `.groups` argument)
lifeExp_bycountry %>%
   filter(mean_lifeExp == min(mean_lifeExp) | mean_lifeExp == max(mean_lifeExp))
# A tibble: 2 x 2
 country      mean_lifeExp
 <chr>               <dbl>
1 Iceland              76.5
2 Sierra Leone         36.8

Інший спосіб зробити це - використовувати функцію dplyr - arrange(), яка розташовує рядки в файлі даних відповідно до порядку одного або декількох змінних з файлу даних. Вона має подібний синтаксис до інших функцій з пакету dplyr. Ви можете використовувати desc() всерединіarrange() для сортування в порядку спададння.

lifeExp_bycountry %>%
   arrange(mean_lifeExp) %>%
   head(1)
# A tibble: 1 x 2
 country      mean_lifeExp
 <chr>               <dbl>
1 Sierra Leone         36.8
lifeExp_bycountry %>%
   arrange(desc(mean_lifeExp)) %>%
   head(1)
# A tibble: 1 x 2
 country mean_lifeExp
 <chr>          <dbl>
1 Iceland         76.5

Функція «group _ by ()» дозволяє групувати по множині змінних. Давайте згрупуємо по змінних «рік» і «континент».

gdp_bycontinents_byyear <- gapminder %>%
  group_by(continent, year) %>%
  summarize(mean_gdpPercap = mean(gdpPercap))
`summarise()` regrouping output by 'continent' (override with `.groups` argument)

Це вже досить потужно, але стає ще краще! Ви не обмежуєтеся визначенням 1 нової змінної в summarize().

gdp_pop_bycontinents_byyear <- gapminder %>%
  group_by(continent,year) %>%
  summarize(mean_gdpPercap = mean(gdpPercap),
            sd_gdpPercap = sd(gdpPercap),
            mean_pop = mean(pop),
            sd_pop = sd(pop))
`summarise()` regrouping output by 'continent' (override with `.groups` argument)

count() і n()

Дуже поширеною операцією є підрахунок кількості спостережень для кожної групи. Пакет «dplyr» поставляється з двома пов’язаними функціями, які допомагають з цим.

Наприклад, якщо ми хочемо перевірити кількість країн, включених до набору даних за 2002 рік, ми можемо використовувати функцію count(). Він приймає назву одного або декількох стовпців, які містять групи, які нас цікавлять, і ми можемо відсортувати результат в порядку спадання sort=TRUE:

gapminder %>%
    filter(year == 2002) %>%
    count(continent, sort = TRUE)
  continent  n
1    Africa 52
2      Asia 33
3    Europe 30
4  Americas 25
5   Oceania  2

Якщо нам потрібно використовувати кількість спостережень у розрахунках, функція n() дуже корисна. Наприклад, якщо ми хочемо отримати стандартну помилку тривалості життя на континенті:

gapminder %>%
    group_by(continent) %>%
    summarize(se_le = sd(lifeExp)/sqrt(n()))
`summarise()` ungrouping output (override with `.groups` argument)
# A tibble: 5 x 2
  continent se_le
  <chr>     <dbl>
1 Africa    0.366
2 Americas  0.540
3 Asia      0.596
4 Europe    0.286
5 Oceania   0.775

Ви також можете об’єднати кілька зведених операцій; в цьому випадку обчислюючи minimum, maximum, mean і se тривалості життя кожного континенту в кожній країні:

gapminder %>%
    group_by(continent) %>%
    summarize(
      mean_le = mean(lifeExp),
      min_le = min(lifeExp),
      max_le = max(lifeExp),
      se_le = sd(lifeExp)/sqrt(n()))
`summarise()` ungrouping output (override with `.groups` argument)
# A tibble: 5 x 5
  continent mean_le min_le max_le se_le
  <chr>       <dbl>  <dbl>  <dbl> <dbl>
1 Africa       48.9   23.6   76.4 0.366
2 Americas     64.7   37.6   80.7 0.540
3 Asia         60.1   28.8   82.6 0.596
4 Europe       71.9   43.6   81.8 0.286
5 Oceania      74.3   69.1   81.2 0.775

Використання mutate()

Ми також можемо створювати нові змінні до (або навіть після) узагальнення інформації, використовуючи mutate().

gdp_pop_bycontinents_byyear <- gapminder %>%
  mutate(gdp_billion = gdpPercap*pop/10^9) %>%
  group_by(continent, year) %>%
  summarize(mean_gdpPercap = mean(gdpPercap),
            sd_gdpPercap = sd(gdpPercap),
            mean_pop = mean(pop),
            sd_pop = sd(pop),
            mean_gdp_billion = mean(gdp_billion),
            sd_gdp_billion = sd(gdp_billion))
`summarise()` regrouping output by 'continent' (override with `.groups` argument)

Інші хороші ресурси

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

  • Use the dplyr package to manipulate dataframes.

  • Use select() to choose variables from a dataframe.

  • Use filter() to choose data based on values.

  • Use group_by() and summarize() to work with subsets of data.

  • Use mutate() to create new variables.