habrahabr

Data tidying: Подготовка наборов данных для анализа на конкретных примерах

  • воскресенье, 25 января 2015 г. в 02:11:19
http://habrahabr.ru/post/248741/

Данная статья возникла в результате переработки и перевода информации на русский язык, взятой из двух источников:

  • из статьи «Tidy Data»
  • из соответствующего swirl урока по tidyr package

Для профессионалов в области анализа данных это, возможно, выглядит как давно выученная таблица умножения — вряд ли они найдут здесь что-то новое. А тех, кто как и я только знакомится с данной областью и возможностями языка R, приглашаю продолжить чтение.

Введение

Есть распространенное мнение, что до 80% процесса анализа данных — это время, потраченное на их подготовку. В данной статье мы остановимся только на одном аспекте подготовки данных к анализу — структурирование и приведение в порядок наборов данных, т.н. data tidying.

Перевести термин «tidy data» на русский язык можно как «аккуратные данные» или «упорядоченные данные». Перефразируя Льва Толстого, все упорядоченные наборы данных похожи друг на друга, каждый беспорядочный набор данных беспорядочен по-своему.

Начнем с вопроса, что такое упорядоченный набор данных.

Определение упорядоченного набора данных

Большая часть наборов данных — это таблицы, содержащие строки и столбцы. Наборы данных содержат значения. Обычно это или цифры (количественные данные) или строки (качественные данные). Каждое значение относится с одной стороны к переменной, с другой стороны к соответствующему наблюдению. При этом наблюдения могут группироваться в типы единиц наблюдения (observation units), чтобы обеспечить их раздельное хранение и не допустить возможных несоответствий.

Является ли набор данных упорядоченным или беспорядочным, зависит от того, как строки, столбцы и таблицы соответствуют наблюдениям, переменным и типам единиц наблюдения. Есть три признака упорядоченного набора данных:
  1. Каждая переменная формирует столбец;
  2. Каждое наблюдение формирует строку;
  3. Каждый тип единицы наблюдения формирует таблицу.

Нарушение любого из перечисленных признаков означает что набор данных является беспорядочным.

Упорядочивание наборов данных

В реальном мире получить сразу упорядоченный набор данных вы можете, разве что, случайно. Рассмотрим пять основных проблем в наборах данных и пути их решения с помощью пакетов языка R tidyr и dplyr.

1. В заголовках столбцов находятся значения, а не имена переменных

Рассмотрим набор данных students:

> students
  grade male female
1     A    1      5
2     B    5      0
3     C    5      2
4     D    5      5
5     E    7      4

В первой колонке Grade указаны оценки, которые получили студенты, а во второй и третьей – какое количество парней и девушек соответственно среди них было.

На самом деле в этом наборе данных три переменных – оценка, пол и количество. Значения переменной «пол» содержится в заголовках второго и третьего столбца. Переменная количество описывает, сколько есть студентов для каждой комбинации оценки и пола.

Чтобы упорядочить этот набор данных, нам нужно добиться, чтобы каждый столбец описывал отдельную переменную. Это можно легко сделать с помощью функции Gather:

> gather(students, sex, count, -grade)
   grade    sex count
1      A   male     1
2      B   male     5
3      C   male     5
4      D   male     5
5      E   male     7
6      A female     5
7      B female     0
8      C female     2
9      D female     5
10     E female     4

С помощью функции Gather мы собираем несколько столбцов в пары key-value. В данном случае sex это key, а count – value. Параметр «-grade» означает, что эта переменная в процессе не участвует и остается без изменений.

2. Несколько переменных хранятся в одном столбце

Рассмотрим набор данных Students2:

> students2
  grade male_1 female_1 male_2 female_2
1     A      3        4      3        4
2     B      6        4      3        5
3     C      7        4      3        8
4     D      4        0      8        1
5     E      1        1      2        7

Он похож на предыдущий набор данных. Отличие состоит в том, что здесь есть разделение по двум классам и количество студентов указано с разделением по полу и классу. В дополнение к предыдущей проблеме здесь добавляется новая – переменные «пол» и «класс» хранятся в одном столбце.

В данном случае проблема упорядочивания набора данных решается в два хода. Сначала выводим переменную count, сохраняя объединение переменных пола и класса. Далее разделяем переменные «пол» и «класс» по разным столбцам. Для удобства в мини-скрипте соединяем все действия оператором chain:

students2 %>%
    gather(sex_class, count, -grade) %>%
    separate(sex_class, into = c("sex", "class")) %>%
    print

Результат работы:

   grade    sex class count
1      A   male     1     3
2      B   male     1     6
3      C   male     1     7
4      D   male     1     4
5      E   male     1     1
6      A female     1     4
7      B female     1     4
8      C female     1     4
9      D female     1     0
10     E female     1     1
11     A   male     2     3
12     B   male     2     3
13     C   male     2     3
14     D   male     2     8
15     E   male     2     2
16     A female     2     4
17     B female     2     5
18     C female     2     8
19     D female     2     1
20     E female     2     7


3. Переменные хранятся как в столбцах, так и в строках

Рассмотрим набор данных students3:

> students3
    name    test class1 class2 class3 class4 class5
1  Sally midterm      A   <NA>      B   <NA>   <NA>
2  Sally   final      C   <NA>      C   <NA>   <NA>
3   Jeff midterm   <NA>      D   <NA>      A   <NA>
4   Jeff   final   <NA>      E   <NA>      C   <NA>
5  Roger midterm   <NA>      C   <NA>   <NA>      B
6  Roger   final   <NA>      A   <NA>   <NA>      A
7  Karen midterm   <NA>   <NA>      C      A   <NA>
8  Karen   final   <NA>   <NA>      C      A   <NA>
9  Brian midterm      B   <NA>   <NA>   <NA>      A
10 Brian   final      B   <NA>   <NA>   <NA>      C

Для каждого из пяти студентов у нас есть промежуточная и финальная оценка. При этом каждый обучался в двух классах из пяти возможных.

Проблемы этого набора данных начинаются с того, что имена столбцов class1:class5 содержат значения одной переменной class. Значения столбца test (midterm, final) должны быть переменными и содержать значение grade для каждого студента.
Для решения этой задачи организуем переменную class и запрячем имена столбцов class1:class5 в ее значения. Далее раскроем значения столбца test в переменные final и midterm. И, наконец, уберем избыточность значений переменной класс, оставив там только цифры. Ниже приведен мини-скрипт:

students3 %>%
    gather(class, grade, class1:class5, na.rm = TRUE) %>%
    spread(test, grade) %>%
    mutate(class, class = extract_numeric(class)) %>%
    print

А еще ниже приведены результаты его работы – упорядоченный набор данных:

    name class final midterm
1  Brian     1     B       B
2  Brian     5     C       A
3   Jeff     2     E       D
4   Jeff     4     C       A
5  Karen     3     C       C
6  Karen     4     A       A
7  Roger     2     A       C
8  Roger     5     A       B
9  Sally     1     C       A
10 Sally     3     C       B


4. Несколько типов единиц наблюдения хранятся в одной таблице

Следующий набор данных students4 выглядит практически так же, как упорядоченный students3 из предыдущего примера. Главное отличие – добавились столбцы id и пол.

> students4
    id  name sex class midterm final
1  168 Brian   F     1       B     B
2  168 Brian   F     5       A     C
3  588 Sally   M     1       A     C
4  588 Sally   M     3       B     C
5  710  Jeff   M     2       D     E
6  710  Jeff   M     4       A     C
7  731 Roger   F     2       C     A
8  731 Roger   F     5       B     A
9  908 Karen   M     3       C     C
10 908 Karen   M     4       A     A

Проблема набора заключается в избыточности данных – сочетания (id, name, sex) встречаются по два раза. Решение задачи состоит в разделении набора данных по двум таблицам:

  • В первой будет храниться информация по студентам (id, name, sex)
  • Во второй будет храниться информация по оценкам (id, class, midterm, final)

Собираем таблицу информации по студентам (выбирая нужные столбцы и удаляя дубликаты):

student_info <- students4 %>%
    select(id, name, sex) %>%
    unique() %>%
    print
   id  name sex
1 168 Brian   F
3 588 Sally   M
5 710  Jeff   M
7 731 Roger   F
9 908 Karen   M

Вторую таблицу получаем простым выбором нужных нам полей:

gradebook <- students4 %>%
    select(id, class, midterm, final) %>%
    print
    id class midterm final
1  168     1       B     B
2  168     5       A     C
3  588     1       A     C
4  588     3       B     C
5  710     2       D     E
6  710     4       A     C
7  731     2       C     A
8  731     5       B     A
9  908     3       C     C
10 908     4       A     A

Таблицы связаны между собой полем id (идентификатором студента).

5. Одна единица наблюдения хранится в нескольких таблицах

Приведем пример обратный предыдущему – единица наблюдения хранится в разных таблицах (passed – те, кто экзамен сдал и failed – те, кто не сдал):

> passed
   name class final
1 Brian     1     B
2 Roger     2     A
3 Roger     5     A
4 Karen     4     A
> failed
   name class final
1 Brian     5     C
2 Sally     1     C
3 Sally     3     C
4  Jeff     2     E
5  Jeff     4     C
6 Karen     3     C

При этом информация о том, сдал ли студент экзамен или нет, содержится в самой оценке (A, B – сдал, в других случаях – не сдал).

Объединим информацию в одну таблицу, предварительно добавив столбец status с признаком, сдал ли студент экзамен или нет:

passed <- mutate(passed, status = "passed")
failed <- mutate(failed, status = "failed")
> rbind_list(passed, failed)
Source: local data frame [10 x 4]

    name class final status
1  Brian     1     B passed
2  Roger     2     A passed
3  Roger     5     A passed
4  Karen     4     A passed
5  Brian     5     C failed
6  Sally     1     C failed
7  Sally     3     C failed
8   Jeff     2     E failed
9   Jeff     4     C failed
10 Karen     3     C failed

В результате получили упорядоченный набор данных с признаком passed/failed.

Перечень источников: