Фактори в R: більше, ніж роздратування?


95

Одним з основних типів даних у R є фактори. З мого досвіду фактори - це, в основному, біль, і я ніколи їх не використовую. Я завжди перетворюю символи. Я почуваюся дивним чином, ніби чогось мені не вистачає.

Чи є деякі важливі приклади функцій, які використовують фактори як групувальні змінні, де факторний тип даних стає необхідним? Чи існують конкретні обставини, коли я повинен використовувати фактори?


7
Я додаю цей коментар для початківців користувачів R, які, ймовірно, знайдуть це питання. Нещодавно я написав допис у блозі, який збирає велику частину інформації з наведених нижче відповідей у ​​навчальний посібник з питань, коли, як і чому використовувати фактори в R. gormanalysis.com/?p=115
Бен,

Я завжди припускав, що фактори зберігаються ефективніше, ніж символи - ніби кожен запис був вказівником на рівень. Але тестуючи це, щоб написати це, я виявив, що це неправда!
ізоморфізми

2
@isomorphismes добре, що раніше було правдою, в перші дні R, але це змінилося. Дивіться цей блог: simplystatistics.org/2015/07/24 / ...
MichaelChirico

4
Через 5 років та більше було написано цей "stringsAsFactors: Несанкціонована біографія": simplystatistics.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  

Попереджувальне повідомлення: У model.matrix.default(mt, mf, contrasts):

змінна, Speciesперетворена на afactor

Одна хитра річ - це цілий 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()щоб знизити невикористані рівні фактора для окремого фактора або для кожного фактора в 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 Лонг

Що зробив d$f <- ordered(d$f, c("apples", "grapes", "oranges"))? Я б здогадався, що це впорядковувало їх у фреймі даних, але після того, як я запустив цей рядок і надрукував фрейм даних, нічого не змінюється. Це просто нав'язує внутрішній порядок, хоча друкований порядок не змінюється?
Аддем

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

4
order () створює довільне впорядкування з будь-яких значень - у тому порядку, який, як ви кажете, упорядкований. Прикро, що я використав лексикографічно відсортовані значення, це випадковість. Наприклад, я використовую це для даних, де "Z" погано, "3" добре, але мітки не є числовими або алфавітними - тому я роблю впорядковані (дані, c ("Z", "B", "A", " 0 "," 1 "," 2 "," 3 ")) і тому я можу просто робити дані>" А ", і це щасливі дні.
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 Лонг

3
тепер функція base-R droplevels(). І це не змінює порядок факторів за замовчуванням.
Бен Болкер,

6

Яка химерна назва!

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

Я використовую їх, коли маю дуже великі вектори символів, мало унікальних спостережень. Це може зменшити споживання пам'яті, особливо якщо рядки у векторному символі довші.

PS - Я жартую над заголовком. Я бачив твій твіт. ;-)


1
Тож ви насправді просто використовуєте їх для економії місця для зберігання. Що має сенс.
JD Лонг

13
Ну принаймні раніше ;-). Але кілька символів R-версії тому було переписано для внутрішнього хешування, тому частина цього історичного аргументу тепер недійсна. Однак фактори дуже корисні для групування та моделювання.
Dirk Eddelbuettel

1
Згідно з ?factorним був R-2.6.0, і в ньому сказано: "Цілі значення зберігаються в 4 байтах, тоді як кожному посиланню на рядок символів потрібен покажчик 4 або 8 байт". Ви б заощадили місце при перетворенні у множник, якщо рядку символів потрібно 8 байт?
Джошуа Ульріх

2
N <- 1000; a <- зразок (c ("a", "b", "c"), N, replace = TRUE); print (object.size (a), units = "Kb"); друк (object.size (коефіцієнт (a)), одиниці = "Kb"); 8 Кб 4,5 Кб, тому, здається, все одно економимо трохи місця.
Едуардо Леоні

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

кран (і в сукупності ) покладаються на фактори. Співвідношення інформації та зусиль цих функцій дуже велике.

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

> 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
Це не хороший приклад, тому що всі ці приклади теж працюватимуть із рядками.
hadley
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.