ปัจจัยใน R: มากกว่าความรำคาญ?


95

ประเภทข้อมูลพื้นฐานชนิดหนึ่งใน R คือปัจจัย จากประสบการณ์ของฉันปัจจัยโดยพื้นฐานแล้วเป็นความเจ็บปวดและฉันไม่เคยใช้มัน ฉันมักจะแปลงเป็นตัวละคร ฉันรู้สึกแปลก ๆ เหมือนขาดอะไรไป

มีตัวอย่างที่สำคัญบางอย่างของฟังก์ชันที่ใช้ปัจจัยเป็นตัวแปรในการจัดกลุ่มที่ประเภทข้อมูลปัจจัยจำเป็นหรือไม่? มีสถานการณ์เฉพาะที่ฉันควรใช้ปัจจัยหรือไม่?


7
ฉันกำลังเพิ่มความคิดเห็นนี้สำหรับผู้ใช้ R ระดับเริ่มต้นที่มีแนวโน้มจะพบคำถามนี้ ฉันเพิ่งเขียนบล็อกโพสต์ที่รวบรวมข้อมูลจำนวนมากจากคำตอบด้านล่างไว้ในบทช่วยสอนเกี่ยวกับเวลาวิธีและเหตุผลที่ต้องใช้ปัจจัยใน R. gormanalysis.com/?p=115
Ben

ฉันเคยคิดว่าปัจจัยต่างๆถูกจัดเก็บอย่างมีประสิทธิภาพมากกว่าตัวอักษรราวกับว่าแต่ละรายการเป็นตัวชี้ไปยังระดับ แต่จากการทดสอบเพื่อเขียนสิ่งนี้ฉันพบว่ามันไม่จริง!
isomorphismes

2
@isomorphismes ดีที่เคยเป็นจริงในช่วงก่อนหน้าของ R แต่นั่นก็เปลี่ยนไป ดูโพสต์ในบล็อกนี้: simplystatistics.org/2015/07/24/…
MichaelChirico

4
5 ปีต่อมามีการเขียน "stringsAsFactors: An unauthorized biography": 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แปลงเป็น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.frameพฤติกรรมที่[.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()การลดระดับปัจจัยที่ไม่ได้ใช้สำหรับแต่ละปัจจัยหรือสำหรับทุกปัจจัยใน 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"))ทำอย่างไร ฉันคงเดาได้ว่ามันเรียงลำดับสิ่งเหล่านี้ใน data frame แต่หลังจากที่ฉันรันบรรทัดนั้นและพิมพ์ data frame ก็ไม่มีอะไรเปลี่ยนแปลง มันกำหนดคำสั่งภายในแม้ว่าคำสั่งพิมพ์จะไม่เปลี่ยนแปลงหรือไม่?
Addem

... ใช่ฉันคิดว่าสิ่งที่ฉันเขียนเป็นประโยคที่ถูกต้อง หากฉันเข้าใจประเด็นของคุณคุณกำลังแสดงให้เราเห็นว่าคุณสามารถกำหนดการจัดลำดับตามปัจจัยซึ่งเป็นสิ่งที่คุณไม่สามารถทำได้สำหรับสตริง
Addem

4
order () สร้างคำสั่งโดยพลการจากค่าใด ๆ - ตามลำดับที่คุณบอกว่าได้รับคำสั่ง โชคไม่ดีที่ฉันใช้ค่าที่เรียงตามพจนานุกรมนั่นเป็นเรื่องบังเอิญ ตัวอย่างเช่นฉันใช้สิ่งนี้สำหรับข้อมูลที่ "Z" ไม่ดี "3" ดี แต่ป้ายกำกับไม่ใช่ตัวเลขหรือตัวอักษร - ดังนั้นฉันจึงเรียงลำดับ (data, c ("Z", "B", "A", " 0 "," 1 "," 2 "," 3 ")) แล้วฉันก็ทำได้แค่ data>" A "และก็เป็นวันที่มีความสุข
mdsumner

19

factorมีความคล้ายคลึงกับประเภทที่แจกแจงในภาษาอื่นมากที่สุด การใช้งานที่เหมาะสมสำหรับตัวแปรซึ่งสามารถรับชุดค่าที่กำหนดไว้ได้เพียงชุดเดียว ในกรณีเหล่านี้ค่าที่อนุญาตที่เป็นไปได้อาจไม่ปรากฏในชุดข้อมูลใดชุดหนึ่งและระดับ "ว่าง" จะสะท้อนให้เห็นอย่างถูกต้อง

ลองพิจารณาตัวอย่างบางส่วน สำหรับข้อมูลบางอย่างที่รวบรวมทั่วสหรัฐอเมริการัฐควรได้รับการบันทึกเป็นปัจจัย ในกรณีนี้ความจริงที่ว่าไม่มีการรวบรวมคดีจากรัฐใดรัฐหนึ่งนั้นเกี่ยวข้อง อาจมีข้อมูลจากสถานะนั้น แต่เกิดขึ้น (ไม่ว่าด้วยเหตุผลใดซึ่งอาจเป็นเหตุผลที่น่าสนใจ) ที่จะไม่เป็นเช่นนั้น หากบ้านเกิดถูกรวบรวมก็จะไม่เป็นปัจจัย ไม่มีการระบุไว้ล่วงหน้าของบ้านเกิดที่เป็นไปได้ หากข้อมูลถูกรวบรวมจากสามเมืองแทนที่จะเป็นระดับประเทศเมืองจะเป็นปัจจัยหนึ่ง: มีสามทางเลือกที่ได้รับตั้งแต่เริ่มแรกและหากไม่พบกรณี / ข้อมูลที่เกี่ยวข้องในหนึ่งในสามเมืองนั้นแสดงว่าเกี่ยวข้อง

ลักษณะอื่น ๆ ของfactors เช่นการให้วิธีการจัดเรียงตามอำเภอใจให้กับชุดของสตริงเป็นลักษณะรองที่มีประโยชน์ของfactors แต่ไม่ใช่เหตุผลของการดำรงอยู่


3
+1. ไบรอันฉันคิดว่าคุณโดนตอกตะปูที่หัวโดยที่ระดับการจับภาพไม่มีอยู่ในข้อมูล
Ricardo Saporta

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
droplevels()ตอนนี้จะมีฟังก์ชั่นฐาน-R และจะไม่เรียงลำดับปัจจัยใหม่ตามค่าเริ่มต้น
Ben Bolker

6

ช่างเป็นชื่อที่น่ากลัว!

ฉันเชื่อว่าฟังก์ชันการประมาณค่าจำนวนมากช่วยให้คุณใช้ปัจจัยเพื่อกำหนดตัวแปรดัมมี่ได้อย่างง่ายดาย ... แต่ฉันไม่ได้ใช้เพื่อสิ่งนั้น

ฉันใช้มันเมื่อฉันมีเวกเตอร์อักขระขนาดใหญ่มากโดยมีการสังเกตที่ไม่ซ้ำกันเพียงไม่กี่อย่าง สิ่งนี้สามารถลดการใช้หน่วยความจำโดยเฉพาะอย่างยิ่งหากสตริงในเวกเตอร์อักขระยาวขึ้น - ish

PS - ฉันล้อเล่นเกี่ยวกับชื่อเรื่อง ฉันเห็นทวีตของคุณ ;-)


1
ดังนั้นคุณเพียงแค่ใช้มันเพื่อประหยัดพื้นที่จัดเก็บ ที่สมเหตุสมผล
JD Long

13
อย่างน้อยก็เคย ;-) แต่การจัดเก็บอักขระรุ่น R ไม่กี่รุ่นที่ผ่านมาถูกเขียนใหม่ให้แฮชภายในดังนั้นส่วนหนึ่งของข้อโต้แย้งในประวัติศาสตร์นี้จึงเป็นโมฆะ ปัจจัยยังคงมีประโยชน์มากสำหรับการจัดกลุ่มและการสร้างแบบจำลอง
Dirk Eddelbuettel

1
ตามที่ระบุ?factorไว้คือ R-2.6.0 และมีข้อความว่า "ค่าจำนวนเต็มจะถูกเก็บไว้ใน 4 ไบต์ในขณะที่การอ้างอิงถึงสตริงอักขระแต่ละครั้งต้องมีตัวชี้ 4 หรือ 8 ไบต์" คุณจะประหยัดพื้นที่ในการแปลงเป็นตัวประกอบหรือไม่หากสตริงอักขระต้องการ 8 ไบต์
Joshua Ulrich

2
N <- 1000; a <- ตัวอย่าง (c ("a", "b", "c"), N แทนที่ = TRUE); พิมพ์ (object.size (a), units = "Kb"); พิมพ์ (object.size (factor (a)), units = "Kb"); 8 Kb 4.5 Kb ดูเหมือนว่าจะประหยัดพื้นที่ได้บ้าง
Eduardo Leoni

2
@Eduardo ฉันมี 4Kb เทียบกับ 4.2Kb สำหรับN=100000ฉันได้ 391.5 Kb เทียบกับ 391.8 Kb ดังนั้นปัจจัยจึงใช้หน่วยความจำเพิ่มขึ้นเล็กน้อย
Marek

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

การแตะ (และรวม ) ขึ้นอยู่กับปัจจัย อัตราส่วนข้อมูลต่อความพยายามของฟังก์ชันเหล่านี้สูงมาก

ตัวอย่างเช่นในโค้ดบรรทัดเดียว (การโทรเพื่อกดด้านล่าง) คุณจะได้รับราคาเฉลี่ยของเพชรด้วยการเจียระไนและสี:

> 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.