Czynniki w R: więcej niż irytacja?

95

Jednym z podstawowych typów danych w R są współczynniki. Z mojego doświadczenia wynika, że ​​czynniki są w zasadzie uciążliwe i nigdy ich nie używam. Zawsze konwertuję na postacie. Dziwnie czuję, że czegoś mi brakuje.

Czy istnieją ważne przykłady funkcji, które wykorzystują czynniki jako zmienne grupujące, w przypadku których typ danych czynnika staje się konieczny? Czy istnieją szczególne okoliczności, w których powinienem używać współczynników?

JD Long
źródło
7
Dodaję ten komentarz dla początkujących użytkowników języka R, którzy prawdopodobnie znajdą to pytanie. Niedawno napisałem post na blogu, w którym wiele informacji z poniższych odpowiedzi jest kompilacją poradnika instruktażowego na temat tego, kiedy, jak i dlaczego należy używać współczynników w R. gormanalysis.com/?p=115
Ben,
Zawsze zakładałem, że czynniki są zapisywane wydajniej niż znaki - tak jakby każdy wpis był wskaźnikiem poziomu. Ale po przetestowaniu tego, aby to zapisać, okazało się, że to nieprawda!
isomorphismes
2
@isomorphismes dobrze, że stosowane aby mogło być prawdziwe, we wcześniejszych dniach R, ale to się zmieniło. Zobacz ten post na blogu: simplystatistics.org/2015/07/24/...
MichaelChirico
4
Ponad 5 lat później napisano to „stringiAsFactors: nieautoryzowana biografia”: simplystatistics.org/2015/07/24/...
JD Long

Odpowiedzi:

49

Powinieneś używać współczynników. Tak mogą być ból, ale moja teoria jest taka, że 90% tego, dlaczego są one uciążliwe jest bo read.tablei read.csvargument stringsAsFactors = TRUEdomyślnie (a większość użytkowników przegap tej subtelności). Twierdzę, że są przydatne, ponieważ pakiety dopasowania modeli, takie jak lme4, używają współczynników i współczynników uporządkowanych, aby w różny sposób dopasować modele i określić rodzaj kontrastów, których należy użyć. Pakiety graficzne również używają ich do grupowania według. ggplota większość funkcji dopasowywania modeli wymusza przekształcenie wektorów znaków na czynniki, więc wynik jest taki sam. Jednak w kodzie pojawiają się ostrzeżenia:

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  

Komunikat ostrzegawczy: W model.matrix.default(mt, mf, contrasts):

zmienna Speciesprzekonwertowana nafactor

Jedna trudna sprawa to całość drop=TRUE. W wektorach działa to dobrze, aby usunąć poziomy czynników, których nie ma w danych. Na przykład:

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

Jednak w przypadku data.frames zachowanie [.data.frame()jest inne: zobacz tego e-maila lub ?"[.data.frame". Używanie drop=TRUEna data.frames nie działa tak, jak można sobie wyobrazić:

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

Na szczęście możesz łatwo upuścić współczynniki, droplevels()aby zrzucić niewykorzystane poziomy współczynników dla pojedynczego czynnika lub dla każdego czynnika w a data.frame(od R 2.12):

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

W ten sposób możesz powstrzymać wybrane poziomy przed pojawieniem się ggplotlegend.

Wewnętrznie factors są liczbami całkowitymi z wektorem znaków na poziomie atrybutu (zobacz attributes(iris$Species)i class(attributes(iris$Species)$levels)), który jest czysty. Gdybyś musiał zmienić nazwę poziomu (i używałeś ciągów znaków), byłaby to znacznie mniej wydajna operacja. Często zmieniam nazwy poziomów, szczególnie w przypadku ggplotlegend. Jeśli sfałszujesz czynniki za pomocą wektorów znaków, istnieje ryzyko, że zmienisz tylko jeden element i przypadkowo utworzysz oddzielny nowy poziom.

Vince
źródło
1
stringsAsFactorsnie jest funkcją.
IRTFM,
30

uporządkowane czynniki są niesamowite, jeśli przypadkiem kocham pomarańcze i nienawidzę jabłek, ale nie mam nic przeciwko winogronom, nie muszę zarządzać jakimś dziwnym indeksem, żeby to powiedzieć:

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", ]
mdsumner
źródło
to zgrabna aplikacja. Nigdy o tym nie myślałem.
JD Long
Co to d$f <- ordered(d$f, c("apples", "grapes", "oranges"))zrobił? Domyśliłbym się, że zamówił je w ramce danych, ale po uruchomieniu tej linii i wydrukowaniu ramki danych nic się nie zmienia. Czy po prostu narzuca zamówienie wewnętrzne, mimo że wydrukowane zamówienie się nie zmienia?
Addem
... Tak, myślę, że to, co napisałem, było jak poprawne zdanie. Jeśli rozumiem twój punkt widzenia, pokazujesz nam, że możesz przypisać kolejność współczynników, czego nie możesz zrobić dla łańcuchów.
Addem
4
order () tworzy dowolną kolejność z dowolnych wartości - w kolejności, w jakiej mówisz, że są uporządkowane. Szkoda, że ​​użyłem wartości posortowanych leksykograficznie, to zbieg okoliczności. Na przykład używam tego do danych, w których „Z” jest złe, „3” jest dobre, ale etykiety nie są numeryczne ani alfabetyczne - tak więc robię uporządkowanie (dane, c („Z”, „B”, „A”, „ 0 "," 1 "," 2 "," 3 ")), więc mogę po prostu zrobić dane>" A "i są szczęśliwe dni.
mdsumner
19

A factorjest najbardziej analogiczne do typu wyliczonego w innych językach. Jego właściwe użycie dotyczy zmiennej, która może przyjmować tylko jeden z określonych zestawów wartości. W takich przypadkach nie każda możliwa dozwolona wartość może występować w jakimkolwiek konkretnym zbiorze danych, a „puste” poziomy dokładnie to odzwierciedlają.

Rozważ kilka przykładów. W przypadku niektórych danych, które zostały zebrane w całych Stanach Zjednoczonych, jako czynnik należy podać stan. W tym przypadku istotny jest fakt, że żadne sprawy nie zostały odebrane z danego państwa. Mogły być dane z tego stanu, ale zdarzyło się (z jakiegokolwiek powodu, który może być powodem zainteresowania), że ich nie było. Gdyby zebrano rodzinne miasto, nie miałoby to znaczenia. Nie ma z góry określonego zestawu możliwych miast rodzinnych. Gdyby dane były zbierane z trzech miast, a nie z całego kraju, to miasto byłoby czynnikiem: na początku podano trzy opcje, a jeśli w jednym z tych trzech miast nie znaleziono żadnych odpowiednich przypadków / danych, jest to istotne.

Inne aspekty factors, takie jak zapewnienie sposobu nadania arbitralnej kolejności zbioru ciągów, są użytecznymi cechami drugorzędnymi factors, ale nie są powodem ich istnienia.

Brian Diggs
źródło
3
+1. Brian, myślę, że trafiłeś w sedno z przechwytywaniem poziomów, których nie ma w danych.
Ricardo Saporta,
13

Czynniki są fantastyczne, gdy ktoś wykonuje analizę statystyczną i faktycznie eksploruje dane. Jednak wcześniej, gdy ktoś czyta, czyści, rozwiązuje problemy, łączy i ogólnie manipuluje danymi, czynniki są totalnym bólem. Niedawno, podobnie jak w ciągu ostatnich kilku lat, wiele funkcji uległo poprawie, aby lepiej radzić sobie z czynnikami. Na przykład, rbind ładnie się z nimi gra. Nadal uważam za uciążliwe pozostawienie pustych poziomów po funkcji podzbioru.

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

Wiem, że łatwo jest przekodować poziomy współczynnika i zmienić etykiety, a także są wspaniałe sposoby na zmianę kolejności poziomów. Mój mózg po prostu ich nie pamięta i za każdym razem, kiedy go używam, muszę się uczyć na nowo. Przekodowywanie powinno być o wiele łatwiejsze niż jest.

Funkcje łańcuchowe języka R są dość łatwe i logiczne w użyciu. Więc kiedy manipuluję, generalnie wolę postacie od czynników.

Farrel
źródło
1
Czy masz przykłady analiz statystyk, które wykorzystują czynniki?
JD Long
3
jest teraz funkcja podstawowa-R droplevels(). I domyślnie nie zmienia kolejności czynników.
Ben Bolker,
6

Co za złośliwy tytuł!

Uważam, że wiele funkcji estymacji pozwala na łatwe definiowanie zmiennych fikcyjnych za pomocą współczynników ... ale nie używam ich do tego.

Używam ich, gdy mam bardzo duże wektory znaków z kilkoma unikalnymi obserwacjami. Może to zmniejszyć zużycie pamięci, zwłaszcza jeśli łańcuchy w wektorze znakowym są dłuższe.

PS - żartuję z tytułu. Widziałem twój tweet. ;-)

Joshua Ulrich
źródło
1
Więc naprawdę po prostu używasz ich do oszczędzania miejsca. To ma sens.
JD Long
13
Cóż, przynajmniej kiedyś ;-). Ale kilka wersji R wcześniej zapisywano na nowo, aby były wewnętrznie haszowane, więc część tego historycznego argumentu jest teraz nieważna. Mimo to czynniki są bardzo przydatne do grupowania i modelowania.
Dirk Eddelbuettel
1
Zgodnie z ?factortym, że był to R-2.6.0 i mówi: „Wartości całkowite są przechowywane w 4 bajtach, podczas gdy każde odwołanie do ciągu znaków wymaga wskaźnika 4 lub 8 bajtów”. Czy zaoszczędziłbyś miejsce na konwersji na czynnik, gdyby ciąg znaków wymagał 8 bajtów?
Joshua Ulrich
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, więc nadal wydaje się, że oszczędza trochę miejsca.
Eduardo Leoni
2
@Eduardo Mam 4Kb vs 4,2Kb. Dla N=100000Dostałem 391,5 Kb vs 391,8 Kb. Tak więc czynnik zajmuje niewiele więcej pamięci.
Marek
1

Czynniki to doskonały mechanizm oznaczania „unikalnych przypadków”. Odtworzyłem to źle wiele razy i pomimo kilku sporadycznych zmarszczek, są one niezwykle silne.

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

Jeśli istnieje lepszy sposób wykonania tego zadania, chciałbym to zobaczyć, nie widzę omawianej możliwości factor.

mdsumner
źródło
-2

tapply (i agregacja ) polegają na czynnikach. Stosunek informacji do nakładu pracy tych funkcji jest bardzo wysoki.

Na przykład w jednej linii kodu (wezwanie do tapply poniżej) możesz uzyskać średnią cenę diamentów według szlifu i koloru:

> 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
Doug
źródło
7
To nie jest dobry przykład, ponieważ wszystkie te przykłady działałyby również z napisami.
hadley,