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

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

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


person JD Long    schedule 10.08.2010    source источник
comment
Я добавляю этот комментарий для начинающих пользователей R, которые могут найти этот вопрос. Недавно я написал сообщение в блоге, в котором большая часть информации из ответов ниже собрана в учебное пособие о том, когда, как и зачем использовать факторы в R. gormanalysis.com/?p=115   -  person Ben    schedule 21.07.2014
comment
Я всегда предполагал, что факторы хранятся более эффективно, чем символы - как если бы каждая запись была указателем на уровень. Но при тестировании, чтобы написать это, я обнаружил, что это неправда!   -  person isomorphismes    schedule 15.04.2015
comment
@isomorphismes Хорошо, что раньше было правдой в ранние дни R, но это изменилось. См. Это сообщение в блоге: simplestatistics.org/2015/07/24/   -  person MichaelChirico    schedule 24.01.2017
comment
Спустя 5+ лет это stringsAsFactors: была написана неавторизованная биография: просто статистика. org / 2015 / 24.07 /   -  person JD Long    schedule 10.02.2017


Ответы (7)


Вам следует использовать коэффициенты. Да, они могут причинять боль, но моя теория состоит в том, что 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  

Предупреждающее сообщение: В 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=TRUE на 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(), чтобы удалить неиспользуемые уровни факторов для отдельного фактора или для каждого фактора в 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 легенд. Если вы подделываете факторы с помощью символьных векторов, есть риск, что вы измените только один элемент и случайно создадите отдельный новый уровень.

person Vince    schedule 10.08.2010
comment
stringsAsFactors не является функцией. - person IRTFM; 26.07.2013

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

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", ]
person mdsumner    schedule 10.08.2010
comment
это отличное приложение. Никогда не думал об этом. - person JD Long; 10.08.2010
comment
Что сделал d$f <- ordered(d$f, c("apples", "grapes", "oranges"))? Я мог бы предположить, что он заказал их во фрейме данных, но после того, как я запустил эту строку и распечатал фрейм данных, ничего не изменилось. Это просто внутренний порядок, даже если напечатанный порядок не меняется? - person Addem; 31.10.2014
comment
... Да, я думаю, что то, что я написал, было чем-то вроде правильного предложения. Если я понимаю вашу точку зрения, вы показываете нам, что вы можете назначать упорядочение по факторам, чего вы не можете сделать для строк. - person Addem; 31.10.2014
comment
Order () создает произвольный порядок из любых значений - в том порядке, в котором вы говорите, что они упорядочены. К сожалению, я использовал лексикографически отсортированные значения, это совпадение. Например, я использую это для данных, где Z - плохо, 3 - хорошо, но метки не являются числовыми или алфавитными, поэтому я упорядочил (data, c (Z, B, A, 0, 1, 2, 3)), и тогда я могу просто набрать данные ›A, и наступили счастливые дни. - person mdsumner; 31.10.2014

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

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

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

person Brian Diggs    schedule 16.10.2013
comment
+1. Брайан, я думаю, вы попали в точку с уровнями захвата, которых нет в данных. - person Ricardo Saporta; 16.10.2013

Факторы прекрасны, когда кто-то проводит статистический анализ и фактически исследует данные. Однако до этого, когда вы читаете, очищаете, устраняете неполадки, объединяете и обычно манипулируете данными, факторы становятся полной болью. Совсем недавно, как и в последние несколько лет, многие функции были улучшены, чтобы лучше справляться с факторами. Например, с ними хорошо играет 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 довольно просты и логичны в использовании. Поэтому при манипулировании я обычно предпочитаю персонажей факторам.

person Farrel    schedule 10.08.2010
comment
У вас есть примеры анализа статистики с использованием факторов? - person JD Long; 10.08.2010
comment
теперь есть функция base-R droplevels(). И он не меняет порядок факторов по умолчанию. - person Ben Bolker; 17.10.2013

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

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

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

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

person Joshua Ulrich    schedule 10.08.2010
comment
Так что вы действительно просто используете их для экономии места для хранения. В этом есть смысл. - person JD Long; 10.08.2010
comment
Ну по крайней мере раньше ;-). Но несколько версий R назад хранилище символов было переписано для внутреннего хеширования, поэтому часть этого исторического аргумента теперь недействительна. Тем не менее факторы очень полезны для группирования и моделирования. - person Dirk Eddelbuettel; 10.08.2010
comment
Согласно ?factor это был R-2.6.0, и в нем говорится, что целочисленные значения хранятся в 4 байтах, тогда как для каждой ссылки на символьную строку требуется указатель размером 4 или 8 байтов. Вы бы сэкономили место при преобразовании в коэффициент, если бы символьной строке требовалось 8 байтов? - person Joshua Ulrich; 10.08.2010
comment
N ‹- 1000; a‹ - выборка (c (a, b, c), N, replace = TRUE); print (object.size (a), units = Kb); print (object.size (factor (a)), units = Kb); 8 Кбайт 4,5 Кбайт, так что, кажется, все еще экономится место. - person Eduardo Leoni; 10.08.2010
comment
@Eduardo Я получил 4 КБ против 4,2 КБ. За N=100000 получилось 391,5 Кб против 391,8 Кб. Фактор требует немного больше памяти. - person Marek; 10.08.2010
comment
Обновление: теперь в R есть какой-то строковый кеш, который устраняет это соображение: stackoverflow.com/questions/18304760/ - person Frank; 16.10.2013

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

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.

person mdsumner    schedule 06.09.2016

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

Например, в одной строке кода (вызов 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
person doug    schedule 10.08.2010
comment
Это не очень хороший пример, потому что все эти примеры также работают со строками. - person hadley; 11.08.2010