Факторы в R: больше, чем раздражение?

95

Один из основных типов данных в R - это факторы. По моему опыту, факторы - это в основном боль, и я никогда не использую их. Я всегда конвертирую в персонажей. Странное чувство, будто я чего-то упускаю.

Есть ли какие-нибудь важные примеры функций, которые используют факторы в качестве группирующих переменных, где тип данных фактор становится необходимым? Есть ли особые обстоятельства, при которых мне следует использовать факторы?

JD Long
источник
7
Я добавляю этот комментарий для начинающих пользователей R, которые могут найти этот вопрос. Недавно я написал сообщение в блоге, в котором большая часть информации из ответов ниже собрана
Бен
Я всегда предполагал, что факторы хранятся более эффективно, чем символы - как если бы каждая запись была указателем на уровень. Но при тестировании, чтобы написать это, я обнаружил, что это неправда!
isomorphismes
2
@isomorphismes хорошо, что используется , чтобы быть правдой, в первые дни R, но это изменилось. Смотрите этот блог: simplystatistics.org/2015/07/24/...
MichaelChirico
4
5+ лет спустя эта «stringsAsFactors: несанкционированная биография» была написана: simplestatistics.org/2015/07/24/…
JD Long

Ответы:

49

Вам следует использовать коэффициенты. Да , они могут быть боль, но моя теория в том , что 90% того , почему они боль, потому что в read.tableи read.csvаргумент stringsAsFactors = TRUEпо умолчанию (и большинство пользователей пропустить эту тонкость). Я говорю, что они полезны, потому что пакеты подгонки моделей, такие как lme4, используют коэффициенты и упорядоченные коэффициенты для дифференциальной подгонки моделей и определения типа используемых контрастов. И пакеты построения графиков также используют их для группировки. ggplotи большинство функций подгонки моделей приводят символьные векторы к факторам, поэтому результат тот же. Однако в конечном итоге в вашем коде появляются предупреждения:

lm(Petal.Length ~ -1 + Species, data=iris)

# Call:
# lm(formula = Petal.Length ~ -1 + Species, data = iris)

# Coefficients:
#     Speciessetosa  Speciesversicolor   Speciesvirginica  
#             1.462              4.260              5.552  

iris.alt <- iris
iris.alt$Species <- as.character(iris.alt$Species)
lm(Petal.Length ~ -1 + Species, data=iris.alt)

# Call:
# lm(formula = Petal.Length ~ -1 + Species, data = iris.alt)

# Coefficients:
#     Speciessetosa  Speciesversicolor   Speciesvirginica  
#             1.462              4.260              5.552  

Предупреждающее сообщение: In model.matrix.default(mt, mf, contrasts):

переменная Speciesпреобразована вfactor

Одна хитрость - это целый drop=TRUEбит. В векторах это хорошо работает для удаления уровней факторов, которых нет в данных. Например:

s <- iris$Species
s[s == 'setosa', drop=TRUE]
#  [1] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [11] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [21] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [31] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [41] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# Levels: setosa
s[s == 'setosa', drop=FALSE]
#  [1] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [11] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [21] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [31] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [41] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# Levels: setosa versicolor virginica

Однако с data.frames [.data.frame()ведет себя иначе: см. Это письмо или ?"[.data.frame". Использование drop=TRUEon data.frames не работает так, как вы думаете:

x <- subset(iris, Species == 'setosa', drop=TRUE)  # susbetting with [ behaves the same way
x$Species
#  [1] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [11] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [21] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [31] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [41] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# Levels: setosa versicolor virginica

К счастью, вы можете легко отбросить факторы, droplevels()чтобы отбросить неиспользуемые уровни факторов для отдельного фактора или для каждого фактора в a data.frame(начиная с R 2.12):

x <- subset(iris, Species == 'setosa')
levels(x$Species)
# [1] "setosa"     "versicolor" "virginica" 
x <- droplevels(x)
levels(x$Species)
# [1] "setosa"

Вот как уберечь выбранные вами уровни от попадания в ggplotлегенды.

Внутренне factors - это целые числа с вектором символов уровня атрибута (см. attributes(iris$Species)И class(attributes(iris$Species)$levels)), который является чистым. Если бы вам пришлось изменить имя уровня (и вы использовали символьные строки), это была бы гораздо менее эффективная операция. И я часто меняю названия уровней, особенно для ggplotлегенд. Если вы подделываете факторы с помощью символьных векторов, есть риск, что вы измените только один элемент и случайно создадите отдельный новый уровень.

Винс
источник
1
stringsAsFactorsэто не функция.
IRTFM
30

Упорядоченные факторы - это здорово, если я люблю апельсины и ненавижу яблоки, но не возражаю против винограда, мне не нужно управлять каким-то странным индексом, чтобы сказать это:

d <- data.frame(x = rnorm(20), f = sample(c("apples", "oranges", "grapes"), 20, replace = TRUE, prob = c(0.5, 0.25, 0.25)))
d$f <- ordered(d$f, c("apples", "grapes", "oranges"))
d[d$f >= "grapes", ]
Мдсумнер
источник
это отличное приложение. Никогда не думал об этом.
JD Long
Что сделал d$f <- ordered(d$f, c("apples", "grapes", "oranges"))? Я бы предположил, что он заказал их во фрейме данных, но после того, как я запустил эту строку и распечатал фрейм данных, ничего не изменилось. Это просто внутренний порядок, даже если напечатанный порядок не меняется?
Аддем
... Да, я думаю, что я написал что-то вроде правильного предложения. Если я понимаю вашу точку зрения, вы показываете нам, что вы можете назначать упорядочение по факторам, чего вы не можете сделать для строк.
Аддем
4
order () создает произвольный порядок из любых значений - в том порядке, в котором вы говорите, что они упорядочены. Очень жаль, что я использовал лексикографически отсортированные значения, это совпадение. Например, я использую это для данных, где «Z» - плохо, «3» - хорошо, но метки не числовые и не алфавитные, поэтому я упорядочил (data, c («Z», «B», «A», « 0 "," 1 "," 2 "," 3 ")) и тогда я могу просто ввести данные>" A ", и наступят счастливые дни.
mdsumner
19

A factorнаиболее похож на нумерованный тип в других языках. Его подходящее использование - для переменной, которая может принимать только одно из заданного набора значений. В этих случаях не все возможные допустимые значения могут присутствовать в каком-либо конкретном наборе данных, и «пустые» уровни точно это отражают.

Рассмотрим несколько примеров. Для некоторых данных, которые были собраны по всей территории Соединенных Штатов, в качестве фактора следует указать штат. В данном случае имеет значение тот факт, что в конкретном штате не было собрано никаких дел. Могли быть данные из этого состояния, но случилось (по какой-то причине, которая может представлять интерес) их не было. Если бы родной город был собран, это не было бы фактором. Не существует заранее определенного набора возможных мест проживания. Если бы данные были собраны из трех городов, а не на национальном уровне, город был бы фактором: есть три варианта, которые были даны в начале, и если не было найдено соответствующих случаев / данных в одном из этих трех городов, это актуально.

Другие аспекты factors, такие как предоставление способа задать произвольный порядок сортировки для набора строк, являются полезными вторичными характеристиками factors, но не являются причиной их существования.

Брайан Диггс
источник
3
+1. Брайан, я думаю, что вы попали в точку с уровнями захвата, которых нет в данных.
Рикардо Сапорта
13

Факторы прекрасны, когда кто-то проводит статистический анализ и фактически исследует данные. Однако до этого, когда вы читаете, очищаете, устраняете неполадки, объединяете и обычно манипулируете данными, факторы становятся полной болью. Совсем недавно, как и в последние несколько лет, многие функции были улучшены, чтобы лучше справляться с факторами. Например, с ними хорошо играет rbind. Я все еще считаю, что оставлять пустые уровни после подмножества - это полная досада.

#drop a whole bunch of unused levels from a whole bunch of columns that are factors using gdata
require(gdata)
drop.levels(dataframe)

Я знаю, что перекодировать уровни фактора и перенастроить метки несложно, а также есть замечательные способы изменить порядок уровней. Мой мозг просто не может их запомнить, и мне приходится переучивать его каждый раз, когда я его использую. Перекодирование должно быть намного проще, чем есть на самом деле.

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

Фаррел
источник
1
У вас есть примеры анализа статистики с использованием факторов?
JD Long
3
теперь есть функция base-R droplevels(). И он не меняет порядок факторов по умолчанию.
Бен Болкер
6

Какой язвительный титул!

Я считаю, что многие функции оценки позволяют вам использовать факторы, чтобы легко определять фиктивные переменные ... но я не использую их для этого.

Я использую их, когда у меня очень большие векторы символов с небольшим количеством уникальных наблюдений. Это может сократить потребление памяти, особенно если строки в векторе символов длиннее.

PS - Шучу по поводу названия. Я видел твой твит. ;-)

Джошуа Ульрих
источник
1
Так что вы действительно просто используете их для экономии места для хранения. Это имеет смысл.
JD Long
13
Ну по крайней мере раньше ;-). Но несколько версий R назад хранилище символов было переписано для внутреннего хеширования, поэтому часть этого исторического аргумента теперь недействительна. Еще факторы очень полезны для группирования и моделирования.
Дирк Эддельбюттель
1
Согласно ?factorR-2.6.0, в нем говорится: «Целочисленные значения хранятся в 4 байтах, тогда как каждая ссылка на символьную строку требует указателя размером 4 или 8 байтов». Вы бы сэкономили место при преобразовании в множитель, если бы символьной строке требовалось 8 байтов?
Джошуа Ульрих
2
N <- 1000; a <- sample (c («a», «b», «c»), N, replace = TRUE); print (object.size (a), units = "Kb"); print (object.size (factor (a)), units = "Kb"); 8 Kb 4.5 Kb, так что, кажется, все еще экономится место.
Эдуардо Леони
2
@Eduardo Я получил 4 КБ против 4,2 КБ. Ибо у N=100000меня получилось 391,5 Кб против 391,8 Кб. Фактор требует немного больше памяти.
Марек
1

Факторы - превосходный двигатель выдачи значков "уникальных случаев". Я много раз воссоздавал это плохо, и, несмотря на то, что иногда появляются несколько морщинок, они очень сильны.

library(dplyr)
d <- tibble(x = sample(letters[1:10], 20, replace = TRUE))

## normalize this table into an indexed value across two tables
id <- tibble(x_u = sort(unique(d$x))) %>% mutate(x_i = row_number())
di <- tibble(x_i = as.integer(factor(d$x)))


## reconstruct d$x when needed
d2 <- inner_join(di, id) %>% transmute(x = x_u)
identical(d, d2)
## [1] TRUE

Если есть лучший способ выполнить эту задачу, я бы хотел его увидеть, я не вижу, чтобы эта возможность factorобсуждалась.

Мдсумнер
источник
-2

tapply агрегат ) полагаются на факторы. Отношение информации к усилию этих функций очень велико.

Например, в одной строке кода (вызов для подачи заявки ниже) вы можете получить среднюю цену бриллиантов по огранке и цвету:

> data(diamonds, package="ggplot2")

> head(dm)

   Carat     Cut    Clarity Price Color
1  0.23     Ideal     SI2   326     E
2  0.21   Premium     SI1   326     E
3  0.23      Good     VS1   327     E


> tx = with(diamonds, tapply(X=Price, INDEX=list(Cut=Cut, Color=Color), FUN=mean))

> a = sort(1:diamonds(tx)[2], decreasing=T)  # reverse columns for readability

> tx[,a]

         Color
Cut         J    I    H    G    F    E    D
Fair      4976 4685 5136 4239 3827 3682 4291
Good      4574 5079 4276 4123 3496 3424 3405
Very Good 5104 5256 4535 3873 3779 3215 3470
Premium   6295 5946 5217 4501 4325 3539 3631
Ideal     4918 4452 3889 3721 3375 2598 2629
дуг
источник
7
Это не очень хороший пример, потому что все эти примеры также работают со строками.
Хэдли