Làm cách nào để chuyển đổi một yếu tố thành số nguyên \ số mà không mất thông tin?


599

Khi tôi chuyển đổi một yếu tố thành một số hoặc số nguyên, tôi nhận được các mã mức cơ bản, không phải các giá trị dưới dạng số.

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

Tôi phải dùng đến pasteđể có được những giá trị thực sự:

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

Có cách nào tốt hơn để chuyển đổi một yếu tố thành số?


6
Các mức của một yếu tố được lưu trữ dưới dạng kiểu dữ liệu ký tự ( attributes(f)), vì vậy tôi không nghĩ có gì sai as.numeric(paste(f)). Có lẽ sẽ tốt hơn khi nghĩ tại sao (trong bối cảnh cụ thể) bạn đang nhận được một yếu tố ở nơi đầu tiên, và cố gắng ngăn chặn điều đó. Ví dụ, là decđối số trong read.tabletập hợp chính xác?
CJB

Nếu bạn sử dụng một khung dữ liệu, bạn có thể sử dụng chuyển đổi từ hablar. df %>% convert(num(column)). Hoặc nếu bạn có một vectơ yếu tố bạn có thể sử dụngas_reliable_num(factor_vector)
davsjob

Câu trả lời:


711

Xem phần Cảnh báo của ?factor:

Cụ thể, as.numericáp dụng cho một yếu tố là vô nghĩa, và có thể xảy ra bằng cách ép buộc ngầm. Để chuyển đổi một yếu tố fthành xấp xỉ các giá trị số ban đầu của nó, as.numeric(levels(f))[f]được khuyến nghị và hiệu quả hơn một chút so với as.numeric(as.character(f)).

Câu hỏi thường gặp về R có lời khuyên tương tự .


Tại sao as.numeric(levels(f))[f]hiệu quả hơn as.numeric(as.character(f))?

as.numeric(as.character(f))là hiệu quả as.numeric(levels(f)[f]), vì vậy bạn đang thực hiện chuyển đổi thành số trên length(x)các giá trị, thay vì trên nlevels(x)các giá trị. Sự khác biệt tốc độ sẽ rõ ràng nhất đối với các vectơ dài với một vài cấp độ. Nếu các giá trị chủ yếu là duy nhất, tốc độ sẽ không có nhiều khác biệt. Tuy nhiên, bạn thực hiện chuyển đổi, thao tác này không chắc là nút cổ chai trong mã của bạn, vì vậy đừng quá lo lắng về nó.


Một số thời gian

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
Để biết thời gian, hãy xem câu trả lời này: stackoverflow.com/questions/6979625/ từ
Ari B. Friedman

3
Rất cám ơn cho giải pháp của bạn. Tôi có thể hỏi tại sao as.numeric (level (f)) [f] chính xác hơn và nhanh hơn không? Cảm ơn.
Sam

7
@Sam as.character (f) yêu cầu "tra cứu nguyên thủy" để tìm hàm as.character.factor (), được định nghĩa là as.numeric (level (f)) [f].
Jonathan

12
khi áp dụng as.numeric (level (f)) [f] OR as.numeric (as.character (f)), tôi có một thông báo cảnh báo: Thông báo cảnh báo: NA được giới thiệu bằng cách ép buộc. Bạn có biết vấn đề có thể ở đâu không? cảm ơn bạn !
maycca

@maycca bạn đã khắc phục vấn đề này chưa?
user08041991

91

R có một số hàm tiện lợi (không có giấy tờ) để chuyển đổi các yếu tố:

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

Nhưng thật khó chịu, không có gì để xử lý yếu tố -> chuyển đổi số . Là một phần mở rộng của câu trả lời của Joshua Ulrich, tôi sẽ đề nghị khắc phục thiếu sót này bằng định nghĩa về chức năng thành ngữ của riêng bạn:

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

rằng bạn có thể lưu trữ ở đầu tập lệnh của mình hoặc thậm chí tốt hơn trong .Rprofiletệp của mình .


14
Không có gì để xử lý chuyển đổi yếu tố thành số nguyên (hoặc số) vì dự kiến ​​sẽ as.integer(factor)trả về mã số nguyên cơ bản (như được hiển thị trong phần ví dụ của ?factor). Có thể ổn khi xác định chức năng này trong môi trường toàn cầu của bạn, nhưng bạn có thể gây ra sự cố nếu bạn thực sự đăng ký nó như một phương thức S3.
Joshua Ulrich

1
Đó là một điểm tốt và tôi đồng ý: việc xác định lại hoàn toàn yếu tố-> chuyển đổi số có khả năng gây rối rất nhiều thứ. Tôi thấy mình đã viết factor->numericchuyển đổi rườm rà rất nhiều trước khi nhận ra rằng thực tế đó là một thiếu sót của R: một số chức năng tiện lợi nên có sẵn ... Gọi nó as.numeric.factorcó ý nghĩa với tôi, nhưng YMMV.
Jealie

4
Nếu bạn thấy mình làm điều đó rất nhiều , thì bạn nên làm gì đó ngược dòng để tránh tất cả cùng nhau.
Joshua Ulrich

2
as.numeric.factor trả về NA?
jO.

@jO.: trong trường hợp bạn đã sử dụng một cái gì đó như v=NA;as.numeric.factor(v)hoặc v='something';as.numeric.factor(v), thì nó sẽ, nếu không, bạn có một điều kỳ lạ đang xảy ra ở đâu đó.
Jealie

33

Cách dễ nhất là sử dụng unfactorchức năng từ gói varhandle

unfactor(your_factor_variable)

Ví dụ này có thể là một khởi đầu nhanh chóng:

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"

Các unfactorcải chức năng để kiểu dữ liệu ký tự đầu tiên và sau đó chuyển đổi sao cho số. Nhập unfactorvào bàn điều khiển và bạn có thể thấy nó ở giữa chức năng. Do đó, nó không thực sự đưa ra một giải pháp tốt hơn những gì người hỏi đã có.
CJB

Phải nói rằng, các cấp độ của một yếu tố thuộc loại nhân vật, vì vậy không có gì bị mất bởi phương pháp này.
CJB

Các unfactorchức năng chăm sóc của những điều mà không thể được chuyển đổi sang số. Kiểm tra các ví dụ tronghelp("unfactor")
Mehrad Mahmoudian

2
@Selrac Tôi đã đề cập rằng chức năng này có sẵn trong gói varhandle , có nghĩa là bạn nên tải gói ( library("varhandle")) trước (như tôi đã đề cập trong dòng đầu tiên của câu trả lời của tôi !!)
Mehrad Mahmoudian

1
@Gregor thêm một phụ thuộc ánh sáng thường không gây hại và tất nhiên nếu bạn đang tìm kiếm cách hiệu quả nhất, hãy viết mã mà bản thân bạn có thể thực hiện nhanh hơn. nhưng như bạn cũng có thể thấy trong nhận xét của mình, điều này không tầm thường vì bạn cũng đã đặt as.numeric()as.character()sai thứ tự;) Đoạn mã của bạn làm gì là biến chỉ số cấp độ của yếu tố thành ma trận ký tự, vì vậy bạn sẽ có gì ở và là một vectơ ký tự có chứa một số số đã được gán cho mức nhất định của yếu tố của bạn. Các chức năng trong gói đó có mặt để ngăn chặn những nhầm lẫn này
Mehrad Mahmoudian

23

Lưu ý: câu trả lời cụ thể này không dành cho việc chuyển đổi các yếu tố có giá trị số sang số, nó là để chuyển đổi các yếu tố phân loại thành số cấp tương ứng của chúng.


Mỗi câu trả lời trong bài đăng này không tạo ra kết quả cho tôi, NA đang được tạo.

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

Điều làm việc cho tôi là thế này -

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

Bạn có chắc là bạn có một yếu tố? Nhìn vào ví dụ này. y<-factor(c("5","15","20","2")); unclass(y) %>% as.numericĐiều này trả về 4,1,3,2, không phải 5,15,20,2. Điều này có vẻ như thông tin không chính xác.
MrFlick

Ok, điều này tương tự như những gì tôi đã cố gắng làm hôm nay: - y2 <-factor (c ("A", "B", "C", "D", "A")); as.numeric (level (y2)) [y2] [1] NA NA NA NA NA NA Thông báo cảnh báo: NA được giới thiệu bằng cách ép buộc trong khi ungroup (y2)%>% as.numeric cho tôi kết quả mà tôi cần.
Indi

4
OK, đó không phải là câu hỏi đã được hỏi ở trên. Trong câu hỏi này, các cấp yếu tố đều là "số". Trong trường hợp của bạn, as.numeric(y)nên đã làm việc tốt, không cần cho unclass(). Nhưng một lần nữa, đó không phải là câu hỏi này. Câu trả lời này không phù hợp ở đây.
MrFlick

3
Chà, tôi thực sự hy vọng nó sẽ giúp được ai đó đang vội như tôi và chỉ đọc tiêu đề!
Indi

1
Nếu bạn có các ký tự đại diện cho các số nguyên làm yếu tố, thì đây là ký tự tôi muốn giới thiệu. đây là người duy nhất làm việc cho tôi
aimme

9

Có thể chỉ trong trường hợp khi các nhãn yếu tố phù hợp với giá trị ban đầu. Tôi sẽ giải thích nó với một ví dụ.

Giả sử dữ liệu là vectơ x:

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

Bây giờ tôi sẽ tạo một yếu tố với bốn nhãn:

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

1) xlà với kiểu double, flà với kiểu nguyên. Đây là mất thông tin đầu tiên không thể tránh khỏi. Các yếu tố luôn được lưu trữ dưới dạng số nguyên.

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

2) Không thể hoàn nguyên về các giá trị ban đầu (10, 20, 30, 40) chỉ fcó sẵn. Chúng ta có thể thấy rằng fchỉ chứa các giá trị nguyên 1, 2, 3, 4 và hai thuộc tính - danh sách các nhãn ("A", "B", "C", "D") và "yếu tố" thuộc tính lớp. Chỉ có bấy nhiêu thôi.

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

Để trở lại các giá trị ban đầu, chúng ta phải biết các giá trị của các mức được sử dụng trong việc tạo ra yếu tố. Trong trường hợp này c(10, 20, 30, 40). Nếu chúng ta biết các mức ban đầu (theo đúng thứ tự), chúng ta có thể trở lại các giá trị ban đầu.

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

Và điều này sẽ chỉ hoạt động trong trường hợp khi nhãn đã được xác định cho tất cả các giá trị có thể có trong dữ liệu gốc.

Vì vậy, nếu bạn sẽ cần các giá trị ban đầu, bạn phải giữ chúng. Nếu không, có khả năng cao là không thể quay lại với họ chỉ từ một yếu tố.


2

Bạn có thể sử dụng hablar::convertnếu bạn có khung dữ liệu. Cú pháp rất dễ:

Mẫu df

library(hablar)
library(dplyr)

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

Giải pháp

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

mang đến cho bạn:

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

Hoặc nếu bạn muốn một cột là số nguyên và một số:

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

kết quả trong:

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

0

Có vẻ như giải pháp as.numeric (level (f)) [f] không còn hoạt động với R 4.0.

Giải pháp thay thế:

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

factor2number(yourFactor)

-1

Từ nhiều câu trả lời tôi có thể đọc, cách duy nhất được đưa ra là mở rộng số lượng biến theo số lượng yếu tố. Nếu bạn có một "thú cưng" khác nhau với các cấp độ "chó" và "mèo", bạn sẽ kết thúc với pet_dog và pet_cat.

Trong trường hợp của tôi, tôi muốn ở cùng một số biến, chỉ bằng cách dịch biến nhân tố thành số, theo cách có thể áp dụng cho nhiều biến có nhiều cấp, ví dụ như cat = 1 và dog = 0.

Vui lòng tìm giải pháp tương ứng dưới đây:

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

Cuối trò chơi, vô tình, tôi thấy trimws()có thể chuyển đổi factor(3:5)sang c("3","4","5"). Sau đó, bạn có thể gọi as.numeric(). Đó là:

as.numeric(trimws(x_factor_var))

3
Có một lý do bạn muốn giới thiệu sử dụng trimwshơn as.characternhư mô tả trong câu trả lời được chấp nhận? Đối với tôi có vẻ như trừ khi bạn thực sự có khoảng trắng bạn cần xóa, trimwschỉ là thực hiện một loạt các biểu thức chính quy không cần thiết để trả về kết quả tương tự.
MrFlick

as.numeric (level (f)) [f] có thể hơi khó hiểu và khó nhớ cho người mới bắt đầu. trimws không có hại.
Jerry T
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.