วิธีการแปลงปัจจัยให้เป็นจำนวนเต็ม \ ตัวเลขโดยไม่สูญเสียข้อมูล?


599

เมื่อฉันแปลงปัจจัยเป็นตัวเลขหรือจำนวนเต็มฉันจะได้รับรหัสระดับพื้นฐานไม่ใช่ค่าเป็นตัวเลข

f <- factor(sample(runif(5), 20, replace = TRUE))
##  [1] 0.0248644019011408 0.0248644019011408 0.179684827337041 
##  [4] 0.0284090070053935 0.363644931698218  0.363644931698218 
##  [7] 0.179684827337041  0.249704354675487  0.249704354675487 
## [10] 0.0248644019011408 0.249704354675487  0.0284090070053935
## [13] 0.179684827337041  0.0248644019011408 0.179684827337041 
## [16] 0.363644931698218  0.249704354675487  0.363644931698218 
## [19] 0.179684827337041  0.0284090070053935
## 5 Levels: 0.0248644019011408 0.0284090070053935 ... 0.363644931698218

as.numeric(f)
##  [1] 1 1 3 2 5 5 3 4 4 1 4 2 3 1 3 5 4 5 3 2

as.integer(f)
##  [1] 1 1 3 2 5 5 3 4 4 1 4 2 3 1 3 5 4 5 3 2

ฉันต้องหันไปใช้pasteเพื่อรับคุณค่าที่แท้จริง:

as.numeric(paste(f))
##  [1] 0.02486440 0.02486440 0.17968483 0.02840901 0.36364493 0.36364493
##  [7] 0.17968483 0.24970435 0.24970435 0.02486440 0.24970435 0.02840901
## [13] 0.17968483 0.02486440 0.17968483 0.36364493 0.24970435 0.36364493
## [19] 0.17968483 0.02840901

มีวิธีที่ดีกว่าในการแปลงปัจจัยให้เป็นตัวเลขหรือไม่?


6
ระดับของปัจจัยจะถูกเก็บเป็นชนิดข้อมูลตัวละครอยู่แล้ว ( attributes(f)) as.numeric(paste(f))ดังนั้นผมจึงไม่คิดว่าจะมีผิดอะไรกับ บางทีมันอาจเป็นการดีกว่าที่จะคิดว่าทำไม (ในบริบทเฉพาะ) คุณได้รับปัจจัยตั้งแต่แรกแล้วลองหยุดสิ่งนั้น เช่นมีการตั้งค่าdecอาร์กิวเมนต์read.tableอย่างถูกต้องหรือไม่
CJB

หากคุณใช้ dataframe คุณสามารถใช้ convert จาก hablar df %>% convert(num(column)). หรือถ้าคุณมีเวกเตอร์แฟคเตอร์คุณสามารถใช้as_reliable_num(factor_vector)
davsjob

คำตอบ:


711

ดูส่วนคำเตือนของ?factor:

โดยเฉพาะอย่างยิ่งการas.numericใช้กับปัจจัยนั้นไม่มีความหมายและอาจเกิดขึ้นได้จากการบีบบังคับโดยปริยาย หากต้องการแปลงปัจจัยfให้ใกล้เคียงกับค่าตัวเลขดั้งเดิมas.numeric(levels(f))[f]ขอแนะนำและมีประสิทธิภาพมากกว่า as.numeric(as.character(f))เล็กน้อย

คำถามที่พบบ่อยในการวิจัยมีคำแนะนำที่คล้ายกัน


ทำไมas.numeric(levels(f))[f]efficent มากกว่าas.numeric(as.character(f))?

as.numeric(as.character(f))มีประสิทธิภาพas.numeric(levels(f)[f])ดังนั้นคุณจึงทำการแปลงเป็นตัวเลขในlength(x)ค่าแทนที่จะเป็นnlevels(x)ค่า ความแตกต่างของความเร็วจะชัดเจนที่สุดสำหรับเวกเตอร์ยาวที่มีระดับน้อย หากค่าส่วนใหญ่ไม่เหมือนใครความเร็วจะไม่แตกต่างกันมากนัก อย่างไรก็ตามคุณทำการแปลงการดำเนินการนี้ไม่น่าจะเป็นคอขวดในรหัสของคุณดังนั้นไม่ต้องกังวลกับมันมากเกินไป


บางเวลา

library(microbenchmark)
microbenchmark(
  as.numeric(levels(f))[f],
  as.numeric(levels(f)[f]),
  as.numeric(as.character(f)),
  paste0(x),
  paste(x),
  times = 1e5
)
## Unit: microseconds
##                         expr   min    lq      mean median     uq      max neval
##     as.numeric(levels(f))[f] 3.982 5.120  6.088624  5.405  5.974 1981.418 1e+05
##     as.numeric(levels(f)[f]) 5.973 7.111  8.352032  7.396  8.250 4256.380 1e+05
##  as.numeric(as.character(f)) 6.827 8.249  9.628264  8.534  9.671 1983.694 1e+05
##                    paste0(x) 7.964 9.387 11.026351  9.956 10.810 2911.257 1e+05
##                     paste(x) 7.965 9.387 11.127308  9.956 11.093 2419.458 1e+05

4
สำหรับการกำหนดเวลาดูคำตอบนี้: stackoverflow.com/questions/6979625/…
Ari B. Friedman

3
ขอบคุณมากสำหรับการแก้ปัญหาของคุณ ฉันขอถามได้ไหมว่าเหตุใดตัวเลข as.numeric (ระดับ (f)) [f] จึงแม่นยำและเร็วขึ้น? ขอบคุณ
แซม

7
@Sam as.character (f) ต้องมี "การค้นหาดั้งเดิม" เพื่อค้นหาฟังก์ชัน as.character.factor () ซึ่งกำหนดไว้เป็น as.numeric (ระดับ (f)) [f]
Jonathan

12
เมื่อใช้ as.numeric (ระดับ (f)) [f] หรือ as.numeric (as.character (f)) ฉันมีข้อความเตือนเกี่ยวกับข้อความเตือน: ข้อความเตือน: NA แนะนำโดยการข่มขู่ คุณรู้หรือไม่ว่าปัญหาเกิดขึ้นที่ใด ขอบคุณ !
maycca

@maycca คุณเอาชนะปัญหานี้หรือไม่
user08041991

91

R มีฟังก์ชั่นอำนวยความสะดวกมากมาย (ไม่มีเอกสาร) สำหรับการแปลงปัจจัย:

  • as.character.factor
  • as.data.frame.factor
  • as.Date.factor
  • as.list.factor
  • as.vector.factor
  • ...

แต่น่ารำคาญไม่มีอะไรที่จะจัดการกับปัจจัย ->การแปลงตัวเลข ในฐานะที่เป็นส่วนขยายของคำตอบของโจชัวอูลริชฉันขอแนะนำให้เอาชนะการละเลยนี้ด้วยนิยามของฟังก์ชันสำนวนของคุณเอง:

as.numeric.factor <- function(x) {as.numeric(levels(x))[x]}

คุณสามารถเก็บไว้ที่จุดเริ่มต้นของสคริปต์หรือดีกว่าใน.Rprofileไฟล์ของคุณ


14
ไม่มีสิ่งใดที่จะจัดการกับการแปลง factor-to-integer (หรือตัวเลข) เนื่องจากคาดว่าas.integer(factor)จะส่งคืนรหัสจำนวนเต็มพื้นฐาน (ดังที่แสดงในส่วนตัวอย่างของ?factor) มันอาจไม่เป็นไรที่จะกำหนดฟังก์ชั่นนี้ในสภาพแวดล้อมทั่วโลกของคุณ แต่คุณอาจทำให้เกิดปัญหาหากคุณลงทะเบียนมันเป็นวิธี S3
Joshua Ulrich

1
นั่นเป็นจุดที่ดีและฉันก็เห็นด้วย: การนิยามใหม่ของการแปลงปัจจัย -> ตัวเลขอย่างละเอียดน่าจะยุ่งเหยิงหลายสิ่งหลายอย่าง ฉันพบว่าตัวเองเขียนการfactor->numericแปลงที่ยุ่งยากมากก่อนที่จะรู้ตัวว่าจริงๆแล้วมันเป็นข้อบกพร่องของ R: ฟังก์ชั่นความสะดวกสบายบางอย่างควรมีให้ใช้ ... การโทรมันas.numeric.factorสมเหตุสมผลสำหรับฉัน แต่ YMMV
Jealie

4
หากคุณพบว่าตัวเองทำอะไรมากมายคุณควรทำบางอย่างที่ต้นน้ำเพื่อหลีกเลี่ยงการรวมตัวกัน
Joshua Ulrich

2
as.numeric.factor ส่งคืน NA หรือไม่
jO

@ โจ: ในกรณีที่คุณใช้สิ่งที่ชอบv=NA;as.numeric.factor(v)หรือv='something';as.numeric.factor(v)ควรเป็นอย่างอื่นมิฉะนั้นคุณจะมีเรื่องแปลก ๆ เกิดขึ้นที่ไหนสักแห่ง
Jealie

33

วิธีที่ง่ายที่สุดคือใช้unfactorฟังก์ชั่นจากแพ็คเกจvarhandle

unfactor(your_factor_variable)

ตัวอย่างนี้สามารถเริ่มต้นอย่างรวดเร็ว:

x <- rep(c("a", "b", "c"), 20)
y <- rep(c(1, 1, 0), 20)

class(x)  # -> "character"
class(y)  # -> "numeric"

x <- factor(x)
y <- factor(y)

class(x)  # -> "factor"
class(y)  # -> "factor"

library(varhandle)
x <- unfactor(x)
y <- unfactor(y)

class(x)  # -> "character"
class(y)  # -> "numeric"

unfactorแปลงฟังก์ชั่นเป็นข้อมูลชนิดตัวอักษรตัวแรกและจากนั้นแปลงกลับไปเป็นตัวเลข พิมพ์unfactorที่คอนโซลและคุณสามารถเห็นมันอยู่ตรงกลางของฟังก์ชั่น ดังนั้นจึงไม่ได้ให้ทางออกที่ดีกว่าสิ่งที่ผู้ถามมีอยู่แล้ว
CJB

ต้องบอกว่าระดับของปัจจัยเป็นประเภทตัวละครอยู่แล้วดังนั้นวิธีนี้จะไม่มีอะไรหายไป
CJB

unfactorฟังก์ชั่นจะดูแลในสิ่งที่ไม่สามารถแปลงเป็นตัวเลข ตรวจสอบตัวอย่างในhelp("unfactor")
Mehrad Mahmoudian

2
@Selrac ฉันได้กล่าวว่าฟังก์ชั่นนี้สามารถใช้ได้ในvarhandleแพคเกจซึ่งหมายความว่าคุณควรจะโหลดแพคเกจ ( library("varhandle")) ครั้งแรก (ที่ผมกล่าวถึงในบรรทัดแรกของคำตอบของฉัน !!)
Mehrad Mahmoudian

1
@Gregor การเพิ่มการพึ่งพาแสงไม่เป็นอันตรายและแน่นอนหากคุณกำลังมองหาวิธีที่มีประสิทธิภาพมากที่สุดการเขียนรหัสด้วยตัวคุณเองอาจทำงานได้เร็วขึ้น แต่อย่างที่คุณเห็นในความคิดเห็นของคุณสิ่งนี้ไม่สำคัญเนื่องจากคุณใส่as.numeric()และas.character()ผิดลำดับ;) สิ่งที่โค้ดของคุณทำคือเปลี่ยนดัชนีระดับของปัจจัยให้เป็นเมทริกซ์อักขระดังนั้นสิ่งที่คุณจะมีและ เป็นเวกเตอร์อักขระที่มีตัวเลขบางตัวที่ได้รับมอบหมายให้ปัจจัยของคุณในระดับหนึ่ง ฟังก์ชั่นในแพ็คเกจนั้นมีไว้เพื่อป้องกันไม่ให้เกิดความสับสน
Mehrad Mahmoudian

23

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


ทุกคำตอบในโพสต์นี้ไม่สามารถสร้างผลลัพธ์ให้ฉันได้มีการสร้าง NA

y2<-factor(c("A","B","C","D","A")); 
as.numeric(levels(y2))[y2] 
[1] NA NA NA NA NA Warning message: NAs introduced by coercion

สิ่งที่ได้ผลสำหรับฉันคือ -

as.integer(y2)
# [1] 1 2 3 4 1

คุณแน่ใจหรือว่าคุณมีปัจจัย ดูตัวอย่างนี้ y<-factor(c("5","15","20","2")); unclass(y) %>% as.numericสิ่งนี้ส่งคืน 4,1,3,2 ไม่ใช่ 5,15,20,2 ดูเหมือนว่าข้อมูลที่ไม่ถูกต้อง
MrFlick

ตกลงนี่คล้ายกับสิ่งที่ฉันพยายามทำในวันนี้: - y2 <-factor (c ("A", "B", "C", "D", "A")); as.numeric (ระดับ (y2)) [y2] [1] นานานานานานาคำเตือน: NA แนะนำโดยการข่มขู่ในขณะที่ไม่เปิดเผย (y2)%>% as.numeric ให้ผลลัพธ์ที่ฉันต้องการ
Indi

4
ตกลงนั่นไม่ใช่คำถามที่ถามข้างต้น ในคำถามนี้ระดับตัวประกอบจะเป็น "ตัวเลข" ทั้งหมด ในกรณีของคุณควรจะได้ทำงานได้ดีที่จำเป็นสำหรับการไม่มีas.numeric(y) unclass()แต่นั่นไม่ใช่สิ่งที่คำถามนี้เกี่ยวกับ คำตอบนี้ไม่เหมาะสมที่นี่
MrFlick

3
ฉันหวังว่ามันจะช่วยให้ใครบางคนที่กำลังรีบอย่างฉันและอ่านเพียงชื่อ!
Indi

1
หากคุณมีตัวละครที่เป็นตัวแทนของจำนวนเต็มนี่เป็นสิ่งที่ฉันอยากจะแนะนำ นี่เป็นคนเดียวที่ทำงานให้ฉัน
aimme

9

เป็นไปได้เฉพาะในกรณีที่ฉลากปัจจัยตรงกับค่าดั้งเดิม ฉันจะอธิบายด้วยตัวอย่าง

สมมติว่าข้อมูลเป็นเวกเตอร์x:

x <- c(20, 10, 30, 20, 10, 40, 10, 40)

ตอนนี้ฉันจะสร้างปัจจัยที่มีสี่ป้ายกำกับ:

f <- factor(x, levels = c(10, 20, 30, 40), labels = c("A", "B", "C", "D"))

1) xเป็นชนิด double, fอยู่กับชนิดจำนวนเต็ม นี่เป็นการสูญเสียข้อมูลครั้งแรกที่หลีกเลี่ยงไม่ได้ ปัจจัยจะถูกเก็บเป็นจำนวนเต็มเสมอ

> typeof(x)
[1] "double"
> typeof(f)
[1] "integer"

2) มันไม่ได้เป็นไปได้ที่จะเปลี่ยนกลับไปใช้ค่าเดิม (10, 20, 30, 40) มีเพียงfใช้ได้ เราจะเห็นว่าfมีค่าจำนวนเต็ม 1, 2, 3, 4 และสองคุณลักษณะ - รายการของป้ายกำกับ ("A", "B", "C", "D") และแอตทริบิวต์ class "factor" ไม่มีอะไรเพิ่มเติม

> str(f)
 Factor w/ 4 levels "A","B","C","D": 2 1 3 2 1 4 1 4
> attributes(f)
$levels
[1] "A" "B" "C" "D"

$class
[1] "factor"

ในการเปลี่ยนกลับเป็นค่าดั้งเดิมเราต้องรู้ค่าระดับที่ใช้ในการสร้างปัจจัย ในกรณีc(10, 20, 30, 40)นี้ หากเรารู้ระดับดั้งเดิม (ตามลำดับที่ถูกต้อง) เราสามารถเปลี่ยนกลับเป็นค่าดั้งเดิมได้

> orig_levels <- c(10, 20, 30, 40)
> x1 <- orig_levels[f]
> all.equal(x, x1)
[1] TRUE

และจะใช้งานได้ก็ต่อเมื่อมีการกำหนดป้ายกำกับสำหรับค่าที่เป็นไปได้ทั้งหมดในข้อมูลต้นฉบับ

ดังนั้นหากคุณต้องการค่าดั้งเดิมคุณต้องเก็บค่าเหล่านั้นไว้ ไม่อย่างนั้นมีโอกาสสูงที่จะไม่สามารถกลับไปหาพวกเขาได้จากปัจจัย


2

คุณสามารถใช้hablar::convertหากคุณมีกรอบข้อมูล ไวยากรณ์เป็นเรื่องง่าย:

ตัวอย่าง df

library(hablar)
library(dplyr)

df <- dplyr::tibble(a = as.factor(c("7", "3")),
                    b = as.factor(c("1.5", "6.3")))

สารละลาย

df %>% 
  convert(num(a, b))

ให้คุณ:

# A tibble: 2 x 2
      a     b
  <dbl> <dbl>
1    7.  1.50
2    3.  6.30

หรือถ้าคุณต้องการให้หนึ่งคอลัมน์เป็นจำนวนเต็มและหนึ่งตัวเลข:

df %>% 
  convert(int(a),
          num(b))

ผลลัพธ์ใน:

# A tibble: 2 x 2
      a     b
  <int> <dbl>
1     7  1.50
2     3  6.30

0

ดูเหมือนว่าโซลูชัน as.numeric (ระดับ (f)) [f] จะไม่ทำงานกับ R 4.0 อีกต่อไป

ทางเลือกอื่น ๆ :

factor2number <- function(x){
    data.frame(levels(x), 1:length(levels(x)), row.names = 1)[x, 1]
}

factor2number(yourFactor)

-1

จากคำตอบมากมายที่ฉันอ่านได้วิธีเดียวที่กำหนดคือการขยายจำนวนตัวแปรตามจำนวนปัจจัย หากคุณมีตัวแปร "สัตว์เลี้ยง" ที่มีระดับ "สุนัข" และ "แมว" คุณจะท้ายด้วย pet_dog และ pet_cat

ในกรณีของฉันฉันต้องการที่จะอยู่กับจำนวนตัวแปรเดียวกันโดยเพียงแค่แปลตัวแปรปัจจัยเป็นตัวเลขในวิธีที่สามารถนำไปใช้กับตัวแปรหลายตัวที่มีหลายระดับดังนั้น cat = 1 และ dog = 0 เป็นต้น

โปรดหาวิธีแก้ปัญหาที่เกี่ยวข้องด้านล่าง:

crime <- data.frame(city = c("SF", "SF", "NYC"),
                    year = c(1990, 2000, 1990),
                    crime = 1:3)

indx <- sapply(crime, is.factor)

crime[indx] <- lapply(crime[indx], function(x){ 
  listOri <- unique(x)
  listMod <- seq_along(listOri)
  res <- factor(x, levels=listOri)
  res <- as.numeric(res)
  return(res)
}
)

-2

ปลายเกมที่บังเอิญผมพบว่าtrimws()สามารถแปลงไปfactor(3:5) จากนั้นคุณสามารถโทรหาc("3","4","5") as.numeric()นั่นคือ:

as.numeric(trimws(x_factor_var))

3
มีเหตุผลที่คุณจะแนะนำให้ใช้trimwsมากกว่าas.characterตามที่อธิบายไว้ในคำตอบที่ยอมรับหรือไม่? ดูเหมือนว่าฉันจะชอบเว้นแต่คุณจะมีช่องว่างที่คุณต้องการที่จะลบtrimwsเป็นจริงจะไปทำงานนิพจน์ปกติที่ไม่จำเป็นเพื่อกลับผลลัพธ์เดียวกัน
MrFlick

as.numeric (ระดับ (f)) [f] อาจจะสับสนเล็กน้อยและยากที่จะจำสำหรับผู้เริ่มต้น trimws ไม่เป็นอันตราย
Jerry T
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.