Главная страница

А. Б. Шипунов, Е. М. Балдин, П. А. Волкова, А. И. Коробейников, С. А. Назарова


Скачать 3.04 Mb.
НазваниеА. Б. Шипунов, Е. М. Балдин, П. А. Волкова, А. И. Коробейников, С. А. Назарова
Анкорrbook
Дата29.09.2022
Размер3.04 Mb.
Формат файлаpdf
Имя файлаrbook.pdf
ТипДокументы
#705644
страница7 из 19
1   2   3   4   5   6   7   8   9   10   ...   19
3.5. Пропущенные данные
Не существует ни совершенных наблюдений, ни совершенных экспе- риментов. Чем больше массив данных, тем больше вероятность встре- тить в нем различные недочеты, прежде всего пропущенные данные,
которые возникают по тысяче причин — от несовершенства методик,
от случайностей во время фиксации данных, от ошибок компьютерных программ и т. д. Строго говоря, пропущенные данные бывают несколь- ких типов. Самый понятный — это «unknown», неизвестное значение,

60
Типы данных когда данные просто не зафиксированы (или потеряны, что, увы, бы- вает нередко). Есть еще «both», когда в процессе наблюдений возник- ло состояние, отвечающее сразу нескольким значениям. Например, ес- ли мы наблюдаем за погодой и отмечаем единицей солнечный день, а нулем — пасмурный, то день с переменной облачностью будет «both»
(обычно это значит, что методика наблюдений разработана плохо). На- конец, «not applicable», неприменимое значение, возникает тогда, когда мы обнаружили нечто, логически не совместимое с тем признаком, ко- торый надо фиксировать. Скажем, мы меряем длины клюва у обитате- лей скворечников, и тут нам попалась белка. У нее нет клюва, значит, и длины его тоже нет. Про эти «философские тонкости» хорошо помнить,
хотя большинство компьютерных программ, занимающихся анализом данных, не различают этих вариантов.
Опыт показывает, что обойтись без пропущенных данных практиче- ски невозможно. Более того, их отсутствие в сколько-нибудь крупном массиве данных может служить основанием для сомнений в их досто- верности.
В R пропущенные данные принято обозначать двумя большими бук- вами латинского алфавита «NA». Предположим, что у нас имеется ре- зультат опроса тех же самых семи сотрудников. Их спрашивали, сколь- ко в среднем часов они спят, при этом один из опрашиваемых отвечать отказался, другой ответил «не знаю», а третьего в момент опроса просто не было на рабочем месте. Так возникли пропущенные данные:
> h <- c(8, 10, NA, NA, 8, NA, 8)
> h
[1]
8 10 NA NA
8 NA
8
Как видим, NA надо вводить без кавычек, а R нимало не смущается,
что среди цифр находится вроде бы текст. Отметим, что пропущенные данные очень часто столь же разнородны, как и в нашем примере. Од- нако кодируются они одинаково, и об этом не нужно забывать. Теперь о том, как надо работать с полученным вектором h. Если мы просто попробуем посчитать среднее значение (функция mean()), то получим:
> mean(h)
[1] NA
И это «идеологически правильно», поскольку функция может по- разному обрабатывать NA, и по умолчанию она просто сигнализирует о том, что с данными что-то не так. Чтобы высчитать среднее от «непро- пущенной» части вектора, можно поступить одним из двух способов:

Выбросы и как их найти
61
> mean(h, na.rm=TRUE)
[1] 8.5
> mean(na.omit(h))
[1] 8.5
Первый способ разрешает функции mean() принимать пропущенные данные, а второй делает из вектора h временный вектор без пропущен- ных данных (они просто выкидываются из вектора). Какой из способов лучше, зависит от ситуации.
Часто возникает еще одна проблема — как сделать подстановку про- пущенных данных, скажем, заменить все NA на среднюю по выборке.
Вот распространенное (но не очень хорошее) решение:
> h[is.na(h)] <- mean(h, na.rm=TRUE)
> h
[1]
8.0 10.0 8.5 8.5 8.0 8.5 8.0
В левой части первого выражения осуществляется индексирование,
то есть выбор нужных значений h — таких, которые являются пропу- щенными (is.na()). После того как выражение выполнено, «старые»
значения исчезают навсегда, поэтому рекомендуем сначала сохранить старый вектор, скажем, под другим названием:
> h.old <- h
> h.old
[1]
8 10 NA NA
8 NA
8
Есть много других способов замены пропущенных значений, в том числе и очень сложные, основанные на регрессионном, а также дискри- минантном анализе. Некоторые из них реализованы в пакетах mice и cat
, существует даже пакет, предоставляющий графический интерфейс для «борьбы» с пропущенными данными, MissingDataGUI.
3.6. Выбросы и как их найти
К сожалению, после набора данных возникают не только «пустые ячейки». Очень часто встречаются просто ошибки. Чаще всего это опе- чатки, которые могут возникнуть при ручном наборе. Если данных немного, то можно попытаться выявить такие ошибки вручную. Хуже,
если объем данных велик — скажем, более тысячи записей. В этом слу- чае могут помочь методы обработки данных, прежде всего те, которые рассчитаны на выявление выбросов (outliers). Самый простой из них —
нахождение минимума и максимума, а для номинальных данных — по-

62
Типы данных строение таблицы частот. К сожалению, такие методы помогают лишь отчасти. Легко найти опечатку в таблице данных роста человека, если кто-то записал 17 см вместо 170 см. Однако ее практически невозможно найти, если вместо 170 см написано 171 см. В этом случае остается на- деяться лишь на статистическую природу данных — чем их больше, тем менее заметны будут ошибки, и на так называемые робастные (устой- чивые к выбросам) методы обработки, о которых мы еще поговорим ниже.
3.7. Меняем данные: основные принципы преобразования
Если в исследовании задействовано несколько разных типов дан- ных — параметрические и непараметрические, номинальные и непре- рывные, проценты и подсчеты и т. п., то самым правильным будет при- вести их к какому-то «общему знаменателю».
Иногда такое преобразование сделать легко. Даже номинальные дан- ные можно преобразовать в непрерывные, если иметь достаточно ин- формации. Скажем, пол (номинальные данные) можно преобразовать в уровень мужского гормона тестостерона в крови (непрерывные); прав- да, для этого нужна дополнительная информация. Распространенный вариант преобразования — обработка дискретных данных так, как буд- то они непрерывные. В целом это безопасно, но иногда приводит к неприятным последствиям. Совершенно неприемлемый вариант — пре- образование номинальных данных в шкальные. Если данные по своей природе не упорядочены, то их искусственное упорядочение может ра- дикально сказаться на результате.
Часто данные преобразуют для того, чтобы они больше походили на параметрические. Если у распределения данных длинные «хвосты»,
если график распределения (как на рис. 12) лишь отчасти «колоколооб- разный», можно прибегнуть к логарифмированию. Это, наверное, самое частое преобразование. В графических командах R есть даже специаль- ный аргумент
..., log="ось"
,
где вместо слова ось надо подставить x или y, и тогда соответствую- щая ось графика отобразится в логарифмическом масштабе.
Вот самые распространенные методы преобразований с указаниями,
как их делать в R (мы предполагаем, что ваши данные находятся в векторе data):
• Логарифмическое: log(data + 1). Если распределение скошено вправо, может дать нормальное распределение. Может также де-

Меняем данные: основные принципы преобразования
63
лать более линейными зависимости между переменными и урав- нивать дисперсии. «Боится» нулей в данных, поэтому рекоменду- ется прибавлять единицу.
• Квадратного корня: sqrt(data). Похоже по действию на логариф- мическое. «Боится» отрицательных значений.
• Обратное: 1/(data + 1). Эффективно для стабилизации диспер- сии. «Боится» нулей.
• Квадратное: data^2. Если распределение скошено влево, может дать нормальное распределение. Линеаризует зависимости и вы- равнивает дисперсии.
• Логит: log(p/(1-p)). Чаще всего применяется к пропорциям. Ли- неаризует так называемую сигмовидную кривую. Кроме логит- преобразования, для пропорций часто используют и арксинус-пре- образование, asin(sqrt(p))
При обработке многомерных данных очень важно, чтобы они были одной размерности. Ни в коем случае нельзя одну колонку в таблице записывать в миллиметрах, а другую — в сантиметрах.
В многомерной статистике широко применяется и нормализация данных — приведение разных колонок к общему виду (например, к од- ному среднему значению). Вот как можно, например, нормализовать два разномасштабных вектора:
> a <- 1:10
> b <- seq(100, 1000, 100)
> d <- data.frame(a, b)
> d a
b
1 1
100 2
2 200 3
3 300 4
4 400 5
5 500 6
6 600 7
7 700 8
8 800 9
9 900 10 10 1000
> scale(d)
a b

64
Типы данных
[1,] -1.4863011 -1.4863011
[2,] -1.1560120 -1.1560120
[3,] -0.8257228 -0.8257228
[4,] -0.4954337 -0.4954337
[5,] -0.1651446 -0.1651446
[6,]
0.1651446 0.1651446
[7,]
0.4954337 0.4954337
[8,]
0.8257228 0.8257228
[9,]
1.1560120 1.1560120
[10,]
1.4863011 1.4863011
Как видим, команда scale() приводит векторы «к общему знамена- телю». Обратите внимание на то, что поскольку вектор b был, по сути,
просто увеличенным в сто раз вектором a, после преобразования они стали совершенно одинаковыми.
3.8. Матрицы, списки и таблицы данных
3.8.1. Матрицы
Матрицы — очень распространенная форма представления данных,
организованных в форме таблицы. Про матрицы в R, в общем, нуж- но знать две важные вещи — во-первых, что они могут быть разной размерности и, во-вторых, что матриц как таковых в R, по сути, нет.
Начнем с последнего. Матрица в R — это просто специальный тип вектора, обладающий некоторыми добавочными свойствами (атрибу- тами), позволяющими интерпретировать его как совокупность строк и столбцов. Предположим, мы хотим создать простейшую матрицу 2 × 2.
Для начала создадим ее из числового вектора:
> m <- 1:4
> m
[1] 1 2 3 4
> ma <- matrix(m, ncol=2, byrow=TRUE)
> ma
[,1] [,2]
[1,]
1 2
[2,]
3 4
> str(ma)
int [1:2, 1:2] 1 3 2 4
> str(m)
int [1:4] 1 2 3 4

Матрицы, списки и таблицы данных
65
Как видно, структура (напомним, структуру любого объекта можно посмотреть при помощи очень важной команды str()) объектов m и ma не слишком различается, различается, по сути, лишь их вывод на экран компьютера. Еще очевиднее единство между векторами и матрицами прослеживается, если создать матрицу несколько иным способом:
> mb <- m
> mb
[1] 1 2 3 4
> attr(mb, "dim") <- c(2,2)
> mb
[,1] [,2]
[1,]
1 3
[2,]
2 4
Выглядит как некий фокус. Однако все просто: мы присваиваем вектору mb атрибут dim («dimensions», размерность) и устанавливаем значение этого атрибута в c(2,2), то есть 2 строки и 2 столбца. Чита- телю предоставляется догадаться, почему матрица mb отличается от матрицы ma (ответ см. в конце главы).
Мы указали лишь два способа создания матриц, в действительности их гораздо больше. Очень популярно, например, «делать» матрицы из векторов-колонок или строк при помощи команд cbind() или rbind().
Если результат нужно «повернуть» на 90 градусов (транспонировать),
используется команда t().
Наиболее распространены матрицы, имеющие два измерения, одна- ко никто не препятствует сделать многомерную матрицу (массив):
> m3 <- 1:8
> dim(m3) <- c(2,2,2)
> m3
, , 1
[,1] [,2]
[1,]
1 3
[2,]
2 4
, , 2
[,1] [,2]
[1,]
5 7
[2,]
6 8

66
Типы данных m3
— это трехмерная матрица (или, по-другому, трехмерный мас- сив). Естественно, показать в виде таблицы ее нельзя, поэтому R вы- водит ее на экран в виде серии таблиц. Аналогично можно создать и четырехмерную матрицу (как встроенные данные Titatic). Многомер- ные матрицы в R принято называть «arrays».
3.8.2. Списки
Списки — еще один важный тип представления данных. Создавать их, особенно на первых порах, скорее всего, не придется, но знать их осо- бенности необходимо — прежде всего потому, что очень многие функции в R выдают «наружу» именно списки.
> l <- list("R", 1:3, TRUE, NA, list("r", 4))
> l
[[1]]
[1] "R"
[[2]]
[1] 1 2 3
[[3]]
[1] TRUE
[[4]]
[1] NA
[[5]]
[[5]][[1]]
[1] "r"
[[5]][[2]]
[1] 4
Видно, что список — это своего рода ассорти. Вектор (и, естественно,
матрица) может состоять из элементов одного и того же типа, а вот список — из чего угодно, в том числе (как видно из примера) и из других списков.
Теперь поговорим про индексирование, или выбор элементов спис- ка. Элементы вектора выбираются, как мы помним, при помощи функ- ции — квадратной скобки:
> h[3]
[1] 8.5

Матрицы, списки и таблицы данных
67
Элементы матрицы выбираются так же, только используются несколь- ко аргументов (для двумерных матриц это номер строки и номер столб- ца — именно в такой последовательности):
>
ma[2, 1]
[1] 3
А вот элементы списка выбираются тремя различными методами.
Во-первых, можно использовать квадратные скобки:
> l[1]
[[1]]
[1] "R"
> str(l[1])
List of 1
$ : chr "R"
Здесь очень важно, что полученный объект тоже будет списком.
Во-вторых, можно использовать двойные квадратные скобки:
> l[[1]]
[1] "R"
> str(l[[1]])
chr "R"
В этом случае полученный объект будет того типа, какого он был бы до объединения в список (поэтому первый объект будет текстовым вектором, а вот пятый — списком).
И в-третьих, для индексирования можно использовать имена эле- ментов списка. Но для этого сначала надо их создать:
> names(l) <- c("first", "second", "third", "fourth", "fifth")
> l$first
[1] "R"
> str(l$first)
chr "R"
Для выбора по имени употребляется знак доллара, а полученный объект будет таким же, как при использовании двойной квадратной скобки. На самом деле имена в R могут иметь и элементы вектора, и строки и столбцы матрицы:

68
Типы данных
> names(w) <- c("Коля", "Женя", "Петя", "Саша", "Катя", "Вася",
+ "Жора")
> w
Коля Женя Петя Саша Катя Вася Жора
69 68 93 87 59 82 72
> rownames(ma) <- c("a1","a2")
> colnames(ma) <- c("b1","b2")
> ma b1 b2
a1 1
2
a2 3
4
Единственное условие — все имена должны быть разными. Одна- ко знак доллара можно использовать только со списками. Элементы вектора по имени можно отбирать так:
> w["Женя"]
Женя
68 3.8.3. Таблицы данных
И теперь о самом важном типе представления данных — таблицах данных (data frame). Именно таблицы данных больше всего похожи на электронные таблицы Excel и аналогов, и поэтому с ними работают ча- ще всего (особенно начинающие пользователи R). Таблицы данных —
это гибридный тип представления, одномерный список из векторов оди- наковой длины
. Таким образом, каждая таблица данных — это список колонок, причем внутри одной колонки все данные должны быть одного типа (а вот сами колонки могут быть разного типа). Проиллюстрируем это на примере созданных ранее векторов:
> d <- data.frame(weight=w, height=x, size=m.o, sex=sex.f)
> d weight height size sex
Коля
69 174
L
male
Женя
68 162
S female
Петя
93 188
XL
male
Саша
87 192
XXL
male
Катя
59 165
S female
Вася
82 168
M
male
Жора
72 172.5
L
male

Матрицы, списки и таблицы данных
69
> str(d)
’data.frame’:
7 obs. of
4 variables:
$ weight: num
69 68 93 87 59 82 72
$ height: num
174 162 188 192 165 168 172.5
$ size
: Ord.factor w/ 5 levels "S"<"M"<"L"<"XL"<..: 3 1 4 5 1 2 3
$ sex
: Factor w/ 2 levels "female","male": 2 1 2 2 1 2 2
Поскольку таблица данных является списком, к ней применимы все методы индексации списков. Таблицы данных можно индексировать и как двумерные матрицы. Вот несколько примеров:
> d$weight
[1] 69 68 93 87 59 82 72
> d[[1]]
[1] 69 68 93 87 59 82 72
> d[,1]
[1] 69 68 93 87 59 82 72
> d[,"weight"]
[1] 69 68 93 87 59 82 72
Очень часто бывает нужно отобрать несколько колонок. Это можно сделать разными способами:
> d[,2:4]
height size sex
Коля
174
L
male
Женя
162
S female
Петя
188
XL
male
Саша
192
XXL
male
Катя
165
S female
Вася
168
M
male
Жора
172.5
L
male
> d[,-1]
height size sex
Коля
174
L
male
Женя
162
S female
Петя
188
XL
male
Саша
192
XXL
male
Катя
165
S female
Вася
168
M
male
Жора
172.5
L
male

70
Типы данных
К индексации имеет прямое отношение еще один тип данных R —
логические векторы
. Как, например, отобрать из нашей таблицы только данные, относящиеся к женщинам? Вот один из способов:
> d[d$sex=="female",]
weight height size sex
Женя
68 162
S female
Катя
59 165
S female
Чтобы отобрать нужные строки, мы поместили перед запятой логи- ческое выражение d$sex==female. Его значением является логический вектор:
> d$sex=="female"
[1] FALSE
TRUE FALSE FALSE
TRUE FALSE FALSE
Таким образом, после того как «отработала» селекция, в таблице данных остались только те строки, которые соответствуют TRUE, то есть строки 2 и 5. Знак «==», а также знаки «&», «|» и «!» используются для замены соответственно «равен?», «и», «или» и «не».
Более сложным случаем отбора является сортировка таблиц дан- ных. Для сортировки одного вектора достаточно применить команду sort()
, а вот если нужно, скажем, отсортировать наши данные сначала по полу, а потом по росту, приходится применить операцию посложнее:
> d[order(d$sex, d$height), ]
weight height size sex
Женя
68 162
S female
Катя
59 165
S female
Вася
82 168
M
male
Жора
72 172.5
L
male
Коля
69 174
L
male
Петя
93 188
XL
male
Саша
87 192
XXL
male
Команда order() создает не логический, а числовой вектор, кото- рый соответствует будущему порядку расположения строк. Подумайте,
как применить команду order() для того, чтобы отсортировать колон- ки получившейся матрицы по алфавиту (см. ответ в конце главы).
* * *
Заключая разговор о типах данных, следует отметить, что эти типы вовсе не так резко отграничены друг от друга. Поэтому если вам трудно

Матрицы, списки и таблицы данных
71
с первого взгляда сказать, к какому именно типу относятся ваши дан- ные, нужно вспомнить главный вопрос — насколько хорошо данные со- относятся с числовой прямой?
Если соотношение хорошее, то данные,
скорее всего, интервальные и непрерывные, если плохое — шкальные или даже номинальные. И во-вторых, не следует забывать, что весьма часто удается найти способ преобразования данных в требуемый тип.
* * *
Ответ к задаче про матрицы
1   2   3   4   5   6   7   8   9   10   ...   19


написать администратору сайта