Yếu tố trong R: nhiều hơn một sự khó chịu?


95

Một trong những kiểu dữ liệu cơ bản trong R là thừa số. Theo kinh nghiệm của tôi, các yếu tố về cơ bản là một nỗi đau và tôi không bao giờ sử dụng chúng. Tôi luôn chuyển đổi sang các ký tự. Tôi cảm thấy kỳ lạ như tôi đang thiếu một cái gì đó.

Có một số ví dụ quan trọng về các hàm sử dụng các yếu tố làm nhóm các biến trong đó kiểu dữ liệu yếu tố trở nên cần thiết không? Có những trường hợp cụ thể khi tôi nên sử dụng các yếu tố không?


7
Tôi thêm nhận xét này cho người dùng R mới bắt đầu, những người có khả năng tìm thấy câu hỏi này. Gần đây tôi đã viết một bài viết trên blog rằng biên dịch nhiều thông tin từ các câu trả lời dưới đây vào một hướng dẫn giảng dạy trên khi nào, như thế nào và tại sao phải sử dụng các yếu tố trong R. gormanalysis.com/?p=115
Ben

Tôi đã luôn giả định rằng các yếu tố được lưu trữ hiệu quả hơn các ký tự — như thể mỗi mục nhập là một con trỏ đến mức. Nhưng khi thử nghiệm nó để viết ra điều này, tôi phát hiện ra điều đó không đúng!
isomorphismes

2
@isomorphismes tốt, điều đó đã từng đúng, trong những ngày đầu của R, nhưng điều đó đã thay đổi. Xem bài đăng trên blog này: simplestosystem.org/2015/07/24/…
MichaelChirico

4
Hơn 5 năm sau, "stringAsFactors: Tiểu sử trái phép" này đã được viết: simplest Statistics.org/2015/07/24/…
JD Long

Câu trả lời:


49

Bạn nên sử dụng các yếu tố. Đúng, chúng có thể là một nỗi đau, nhưng lý thuyết của tôi là 90% lý do tại sao chúng là một nỗi đau là vì read.tableread.csv, lập luận stringsAsFactors = TRUEtheo mặc định (và hầu hết người dùng đều bỏ lỡ sự tinh tế này). Tôi nói rằng chúng hữu ích vì các gói phù hợp mô hình như các yếu tố sử dụng lme4 và các yếu tố có thứ tự để phù hợp với các mô hình một cách khác biệt và xác định loại tương phản để sử dụng. Và các gói đồ thị cũng sử dụng chúng để nhóm theo. ggplotvà hầu hết các hàm điều chỉnh mô hình bắt buộc các vectơ ký tự với các yếu tố, vì vậy kết quả là giống nhau. Tuy nhiên, bạn sẽ nhận được cảnh báo trong mã của mình:

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  

Thông báo cảnh báo: Trong model.matrix.default(mt, mf, contrasts):

biến được Specieschuyển đổi thànhfactor

Một điều khó khăn là toàn bộ drop=TRUEbit. Trong vectơ, điều này hoạt động tốt để loại bỏ các cấp độ của các yếu tố không có trong dữ liệu. Ví dụ:

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

Tuy nhiên , với data.frames, hành vi của lại [.data.frame()khác: xem email này hoặc ?"[.data.frame". Sử dụng drop=TRUEtrên data.frames không hoạt động như bạn tưởng tượng:

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

May mắn thay, bạn có thể loại bỏ các yếu tố một cách dễ dàng bằng droplevels()cách giảm các mức yếu tố không sử dụng cho một yếu tố riêng lẻ hoặc cho mọi yếu tố trong a data.frame(kể từ R 2,12):

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

Đây là cách giữ cho các cấp độ bạn đã chọn không xuất hiện trong ggplottruyền thuyết.

Bên trong, factors là các số nguyên có vectơ ký tự cấp thuộc tính (xem attributes(iris$Species)class(attributes(iris$Species)$levels)), là số nguyên. Nếu bạn phải thay đổi tên cấp (và bạn đang sử dụng chuỗi ký tự), đây sẽ là một hoạt động kém hiệu quả hơn nhiều . Và tôi thay đổi tên cấp độ rất nhiều, đặc biệt là cho các ggplothuyền thoại. Nếu bạn giả mạo các yếu tố bằng vectơ ký tự, có nguy cơ bạn chỉ thay đổi một yếu tố và vô tình tạo ra một cấp độ mới riêng biệt.


1
stringsAsFactorskhông phải là một chức năng.
IRTFM

30

các yếu tố theo thứ tự thật tuyệt vời, nếu tôi yêu cam và ghét táo nhưng không ngại nho, tôi không cần quản lý một số chỉ số kỳ lạ để nói như vậy:

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", ]

đó là một ứng dụng gọn gàng. Không bao giờ nghĩ đến điều đó.
JD Dài

Đã d$f <- ordered(d$f, c("apples", "grapes", "oranges"))làm gì? Tôi có thể đoán rằng nó đã sắp xếp những thứ này trong khung dữ liệu, nhưng sau khi tôi chạy dòng đó và in khung dữ liệu, không có gì thay đổi. Nó chỉ áp đặt một lệnh nội bộ mặc dù lệnh in không thay đổi?
Addem

... Ừ, tôi nghĩ những gì tôi viết là một câu đúng. Nếu tôi hiểu quan điểm của bạn, bạn đang cho chúng tôi thấy rằng bạn có thể chỉ định một thứ tự trên các yếu tố, đây là điều bạn không thể làm đối với chuỗi.
Addem

4
Order () tạo một thứ tự tùy ý từ bất kỳ giá trị nào - theo thứ tự mà bạn nói rằng chúng được sắp xếp. Thật không may là tôi đã sử dụng các giá trị được sắp xếp theo từ điển, đó là một sự trùng hợp. Ví dụ: tôi sử dụng điều này cho dữ liệu trong đó "Z" là xấu, "3" là tốt nhưng các nhãn không phải là số hoặc bảng chữ cái - vì vậy tôi có thứ tự (dữ liệu, c ("Z", "B", "A", " 0 "," 1 "," 2 "," 3 ")) và sau đó tôi có thể làm dữ liệu>" A "và đó là những ngày hạnh phúc.
mdsumner

19

A factorgần giống với kiểu liệt kê trong các ngôn ngữ khác. Cách sử dụng thích hợp của nó là cho một biến chỉ có thể nhận một trong các bộ giá trị được chỉ định. Trong những trường hợp này, không phải mọi giá trị được phép khả thi đều có thể có trong bất kỳ tập hợp dữ liệu cụ thể nào và các mức "trống" phản ánh chính xác điều đó.

Hãy xem xét một số ví dụ. Đối với một số dữ liệu được thu thập trên toàn nước Mỹ, tiểu bang phải được ghi lại như một yếu tố. Trong trường hợp này, thực tế là không có trường hợp nào được thu thập từ một tiểu bang cụ thể là phù hợp. Có thể có dữ liệu từ trạng thái đó, nhưng đã xảy ra (vì bất kỳ lý do gì, có thể là lý do quan tâm) thì không. Nếu quê quán được thu thập thì sẽ không thành yếu tố. Không có một tập hợp các quê quán có thể được nêu trước. Nếu dữ liệu được thu thập từ ba thị trấn thay vì toàn quốc, thị trấn sẽ là một yếu tố: có ba lựa chọn được đưa ra ngay từ đầu và nếu không tìm thấy trường hợp / dữ liệu liên quan nào ở một trong ba thị trấn đó, thì điều đó có liên quan.

Các khía cạnh khác của factors, chẳng hạn như cung cấp một cách để đưa ra một thứ tự sắp xếp tùy ý cho một tập hợp các chuỗi, là những đặc điểm phụ hữu ích của factors, nhưng không phải là lý do tồn tại của chúng.


3
+1. Brian, tôi nghĩ bạn đã đánh trúng đầu với mức độ bắt giữ không có trong dữ liệu.
Ricardo Saporta

13

Các yếu tố thật tuyệt vời khi một người đang thực hiện phân tích thống kê và thực sự khám phá dữ liệu. Tuy nhiên, trước đó khi đọc, dọn dẹp, khắc phục sự cố, hợp nhất và nói chung là thao tác dữ liệu, các yếu tố là một vấn đề hoàn toàn khó khăn. Gần đây, cũng như trong vài năm qua, nhiều chức năng đã được cải thiện để xử lý các yếu tố tốt hơn. Ví dụ, rbind chơi tốt với họ. Tôi vẫn thấy thật phiền toái khi để lại các mức trống sau một hàm tập hợp con.

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

Tôi biết rằng việc mã hóa lại các mức của một yếu tố và điều chỉnh lại các nhãn rất đơn giản và cũng có những cách tuyệt vời để sắp xếp lại các mức. Bộ não của tôi không thể nhớ chúng và tôi phải học lại nó mỗi khi tôi sử dụng nó. Việc ghi lại sẽ dễ dàng hơn rất nhiều.

Các hàm chuỗi của R khá dễ sử dụng và hợp lý. Vì vậy, khi thao tác, tôi thường thích các ký tự hơn các yếu tố.


1
Bạn có ví dụ về phân tích số liệu thống kê sử dụng các yếu tố không?
JD Dài

3
bây giờ có một hàm cơ sở-R droplevels(). Và nó không sắp xếp lại các yếu tố theo mặc định.
Ben Bolker

6

Thật là một tiêu đề khó nghe!

Tôi tin rằng nhiều hàm ước lượng cho phép bạn sử dụng các yếu tố để dễ dàng xác định các biến giả ... nhưng tôi không sử dụng chúng cho điều đó.

Tôi sử dụng chúng khi tôi có các vectơ ký tự rất lớn với ít quan sát duy nhất. Điều này có thể cắt giảm tiêu thụ bộ nhớ, đặc biệt nếu các chuỗi trong vector ký tự dài hơn.

Tái bút - Tôi đang nói đùa về tiêu đề. Tôi đã thấy tweet của bạn. ;-)


1
Vì vậy, bạn thực sự chỉ sử dụng chúng để tiết kiệm không gian lưu trữ. Điều đó có lý.
JD Dài

13
Ít nhất thì nó cũng đã từng ;-). Nhưng một vài phiên bản R trước đây bộ nhớ ký tự đã được viết lại để được băm bên trong nên một phần của lập luận lịch sử này giờ đã vô hiệu. Yếu tố tĩnh rất hữu ích cho việc phân nhóm và lập mô hình.
Dirk Eddelbuettel

1
Theo ?factornó là R-2.6.0 và nó nói, "Giá trị số nguyên được lưu trữ trong 4 byte trong khi mỗi tham chiếu đến chuỗi ký tự cần một con trỏ 4 hoặc 8 byte." Bạn có tiết kiệm không gian chuyển đổi thành thừa số nếu chuỗi ký tự cần 8 byte không?
Joshua Ulrich

2
N <- 1000; a <- sample (c ("a", "b", "c"), N, thay thế = TRUE); print (object.size (a), units = "Kb"); print (object.size (factor (a)), units = "Kb"); 8 Kb 4,5 Kb vì vậy nó có vẻ vẫn tiết kiệm được một số dung lượng.
Eduardo Leoni

2
@Eduardo Tôi có 4Kb so với 4.2Kb. Vì N=100000tôi nhận được 391,5 Kb so với 391,8 Kb. Vì vậy, yếu tố chiếm ít bộ nhớ hơn.
Marek

1

Yếu tố là một công cụ huy hiệu "trường hợp độc nhất" tuyệt vời. Tôi đã tái tạo điều này một cách tồi tệ nhiều lần, và mặc dù đôi khi có một vài nếp nhăn, chúng vẫn cực kỳ mạnh mẽ.

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

Nếu có cách tốt hơn để thực hiện nhiệm vụ này, tôi rất muốn xem nó, tôi không thấy khả năng này được factorthảo luận.


-2

tapply (và tổng hợp ) dựa trên các yếu tố. Tỷ lệ thông tin trên nỗ lực của các chức năng này là rất cao.

Ví dụ: trong một dòng mã (lệnh gọi tapply bên dưới), bạn có thể nhận được giá trung bình của kim cương theo Cắt và Màu:

> 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
Đây không phải là một ví dụ tốt, bởi vì tất cả các ví dụ đó cũng sẽ hoạt động với chuỗi.
hadley
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.