Chuyển đổi cột data.frame từ các yếu tố thành ký tự


352

Tôi có một khung dữ liệu. Hãy gọi anh ta bob:

> head(bob)
                 phenotype                         exclusion
GSM399350 3- 4- 8- 25- 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399351 3- 4- 8- 25- 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399352 3- 4- 8- 25- 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399353 3- 4- 8- 25+ 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399354 3- 4- 8- 25+ 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399355 3- 4- 8- 25+ 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-

Tôi muốn nối các hàng của khung dữ liệu này (đây sẽ là một câu hỏi khác). Nhưng hãy nhìn:

> class(bob$phenotype)
[1] "factor"

BobCác cột là các yếu tố. Ví dụ:

> as.character(head(bob))
[1] "c(3, 3, 3, 6, 6, 6)"       "c(3, 3, 3, 3, 3, 3)"      
[3] "c(29, 29, 29, 30, 30, 30)"

Tôi không bắt đầu hiểu điều này, nhưng tôi đoán đây là những chỉ số về mức độ của các yếu tố của các cột (của triều đình vua caractacus) của bob? Không phải những gì tôi cần.

Kỳ lạ là tôi có thể đi qua các cột bobbằng tay, và làm

bob$phenotype <- as.character(bob$phenotype)

hoạt động tốt Và, sau khi gõ, tôi có thể nhận được data.frame có các cột là ký tự chứ không phải là các yếu tố. Vì vậy, câu hỏi của tôi là: làm thế nào tôi có thể làm điều này tự động? Làm cách nào để chuyển đổi data.frame với các cột yếu tố thành data.frame với các cột ký tự mà không phải đi thủ công qua từng cột?

Câu hỏi thưởng: tại sao cách tiếp cận thủ công hoạt động?


3
sẽ tốt hơn nếu bạn làm cho câu hỏi có thể lặp lại, vì vậy bao gồm cấu trúc của bob.
jangorecki 9/12/2015

Câu trả lời:


362

Chỉ cần theo dõi Matt và Dirk. Nếu bạn muốn tạo lại khung dữ liệu hiện tại của mình mà không thay đổi tùy chọn chung, bạn có thể tạo lại nó bằng một câu lệnh áp dụng:

bob <- data.frame(lapply(bob, as.character), stringsAsFactors=FALSE)

Điều này sẽ chuyển đổi tất cả các biến thành "ký tự" lớp, nếu bạn muốn chỉ chuyển đổi các yếu tố, xem giải pháp của Marek bên dưới .

Như @hadley chỉ ra, những điều sau đây ngắn gọn hơn.

bob[] <- lapply(bob, as.character)

Trong cả hai trường hợp, đưa lapplyra một danh sách; tuy nhiên, do các thuộc tính phép thuật của R, việc sử dụng []trong trường hợp thứ hai giữ lớp data.frame của bobđối tượng, do đó loại bỏ nhu cầu chuyển đổi trở lại data.frame bằng cách sử dụng as.data.frameđối số stringsAsFactors = FALSE.


27
Shane, điều đó cũng sẽ biến các cột số thành ký tự.
Dirk Eddelbuettel

@Dirk: Điều đó đúng, mặc dù không rõ liệu đó có phải là vấn đề ở đây không. Rõ ràng, tạo ra những thứ chính xác lên phía trước là giải pháp tốt nhất. Tôi không nghĩ rằng thật dễ dàng để tự động chuyển đổi các loại dữ liệu qua khung dữ liệu. Một tùy chọn là sử dụng ở trên nhưng sau đó sử dụng type.convertsau khi truyền mọi thứ sang character, sau đó factorsquay lại characterlần nữa.
Shane

Điều này dường như để loại bỏ tên hàng.
piccolbo

2
@piccolbo bạn đã sử dụng bob[] <- trong ví dụ hoặc bob <- ?; cái đầu tiên giữ data.frame; cái thứ hai thay đổi data.frame thành một danh sách, bỏ các tên miền. Tôi sẽ cập nhật câu trả lời
David LeBauer

6
Một biến thể chỉ chuyển đổi các cột yếu tố thành ký tự bằng cách sử dụng một hàm ẩn danh: iris[] <- lapply(iris, function(x) if (is.factor(x)) as.character(x) else {x})
Stefan F

313

Để chỉ thay thế các yếu tố:

i <- sapply(bob, is.factor)
bob[i] <- lapply(bob[i], as.character)

Trong gói dplyr trong phiên bảnmutate_if 0.5.0, chức năng mới đã được giới thiệu :

library(dplyr)
bob %>% mutate_if(is.factor, as.character) -> bob

Gói purrr từ RStudio cung cấp một thay thế khác:

library(purrr)
library(dplyr)
bob %>% map_if(is.factor, as.character) %>% as_tibble -> bob

Không làm việc cho tôi, thật đáng buồn. Không biết tại sao. Có lẽ bởi vì tôi có colnames?
Autumnsault

@mohawkjohn Không nên là vấn đề. Bạn có lỗi hoặc kết quả không như bạn mong đợi?
Marek

2
Lưu ý: purrrDòng trả về một danh sách, không phải a data.frame!
Hoàng gia

Điều này cũng hoạt động nếu bạn đã có imột vectơ colnames().
verbamour

39

Tùy chọn toàn cầu

StringAsFactors: Cài đặt mặc định cho các đối số của data.frame và read.table.

có thể là thứ bạn muốn đặt FALSEtrong các tệp khởi động của mình (ví dụ ~ / .Rprofile). Xin vui lòng xem help(options).


5
Vấn đề với điều này là khi bạn thực thi mã của mình trong môi trường thiếu tệp .Rprofile, bạn sẽ gặp lỗi!
waferthin

4
Tôi có xu hướng gọi nó ở đầu tập lệnh thay vì cài đặt trong .Rprofile.
gregmacfarlane

22

Nếu bạn hiểu cách các yếu tố được lưu trữ, bạn có thể tránh sử dụng các hàm dựa trên ứng dụng để thực hiện việc này. Điều đó hoàn toàn không ngụ ý rằng các giải pháp áp dụng không hoạt động tốt.

Các yếu tố được cấu trúc như các chỉ số số gắn liền với một danh sách 'cấp độ'. Điều này có thể được nhìn thấy nếu bạn chuyển đổi một yếu tố thành số. Vì thế:

> fact <- as.factor(c("a","b","a","d")
> fact
[1] a b a d
Levels: a b d

> as.numeric(fact)
[1] 1 2 1 3

Các số được trả về trong dòng cuối cùng tương ứng với các cấp của yếu tố.

> levels(fact)
[1] "a" "b" "d"

Lưu ý rằng levels()trả về một mảng các ký tự. Bạn có thể sử dụng thực tế này để chuyển đổi dễ dàng và gọn nhẹ các yếu tố thành chuỗi hoặc số như thế này:

> fact_character <- levels(fact)[as.numeric(fact)]
> fact_character
[1] "a" "b" "a" "d"

Điều này cũng hoạt động cho các giá trị số, miễn là bạn gói biểu thức của bạn vào as.numeric().

> num_fact <- factor(c(1,2,3,6,5,4))
> num_fact
[1] 1 2 3 6 5 4
Levels: 1 2 3 4 5 6
> num_num <- as.numeric(levels(num_fact)[as.numeric(num_fact)])
> num_num
[1] 1 2 3 6 5 4

Câu trả lời này không giải quyết được vấn đề, đó là cách tôi chuyển đổi tất cả các cột yếu tố trong khung dữ liệu của mình thành ký tự. as.character(f), là tốt hơn trong cả khả năng đọc và hiệu quả để levels(f)[as.numeric(f)]. Nếu bạn muốn khéo léo, bạn có thể sử dụng levels(f)[f]thay thế. Lưu ý rằng khi chuyển đổi một yếu tố với các giá trị số, bạn sẽ nhận được một số lợi ích từ as.numeric(levels(f))[f]hơn, ví dụ as.numeric(as.character(f)), nhưng điều này là do bạn chỉ phải chuyển đổi các cấp thành số và sau đó tập hợp con. as.character(f)chỉ là tốt như nó là.
De Novo

20

Nếu bạn muốn một khung dữ liệu mới bobctrong đó mọi vectơ nhân tố bobfđược chuyển đổi thành một vectơ ký tự, hãy thử điều này:

bobc <- rapply(bobf, as.character, classes="factor", how="replace")

Nếu sau đó bạn muốn chuyển đổi nó trở lại, bạn có thể tạo một vectơ logic trong đó các cột là các yếu tố và sử dụng nó để áp dụng có chọn lọc yếu tố

f <- sapply(bobf, class) == "factor"
bobc[,f] <- lapply(bobc[,f], factor)

2
+1 để chỉ làm những gì cần thiết (nghĩa là không chuyển đổi toàn bộ data.frame thành ký tự). Giải pháp này rất mạnh đối với data.frame có chứa các loại hỗn hợp.
Joshua Ulrich

3
Ví dụ này phải nằm trong phần 'Ví dụ' cho rapply, như tại: stat.ethz.ch/R-manual/R-devel/l Library / base / html / rapply.html . Bất cứ ai biết làm thế nào để yêu cầu đó là như vậy?
mpettis

Nếu bạn muốn kết thúc với một khung dữ liệu, hãy đơn giản bọc rapply trong một cuộc gọi data.frame (sử dụng chuỗiAsAsactact được đặt thành đối số FALSE)
Các trang web của Taylored

13

Tôi thường làm cho chức năng này ngoài tất cả các dự án của tôi. Nhanh chóng và dễ dàng.

unfactorize <- function(df){
  for(i in which(sapply(df, class) == "factor")) df[[i]] = as.character(df[[i]])
  return(df)
}

8

Một cách khác là chuyển đổi nó bằng cách sử dụng áp dụng

bob2 <- apply(bob,2,as.character)

Và một cái tốt hơn (cái trước là của lớp 'ma trận')

bob2 <- as.data.frame(as.matrix(bob),stringsAsFactors=F)

Theo dõi bình luận của @ Shane: để lấy data.frame, doas.data.frame(lapply(...
aL3xa

7

Cập nhật: Đây là một ví dụ về một cái gì đó không hoạt động. Tôi nghĩ nó sẽ như vậy, nhưng tôi nghĩ rằng tùy chọn StringAsFactors chỉ hoạt động trên các chuỗi ký tự - nó chỉ để lại các yếu tố.

Thử cái này:

bob2 <- data.frame(bob, stringsAsFactors = FALSE)

Nói chung, bất cứ khi nào bạn gặp vấn đề với các yếu tố nên là nhân vật, sẽ có một stringsAsFactorscài đặt ở đâu đó để giúp bạn (bao gồm cả cài đặt toàn cầu).


1
Điều này không hoạt động, nếu anh ta đặt nó khi tạo bobđể bắt đầu (nhưng không phải sau khi thực tế).
Shane

Đúng. Chỉ muốn rõ ràng rằng điều này không giải quyết được vấn đề, nhưng sẽ cảm ơn vì đã lưu ý rằng nó ngăn chặn nó.
Matt Parker

7

Hoặc bạn có thể thử transform:

newbob <- transform(bob, phenotype = as.character(phenotype))

Chỉ cần chắc chắn để đặt mọi yếu tố bạn muốn chuyển đổi thành nhân vật.

Hoặc bạn có thể làm một cái gì đó như thế này và tiêu diệt tất cả các loài gây hại chỉ bằng một đòn:

newbob_char <- as.data.frame(lapply(bob[sapply(bob, is.factor)], as.character), stringsAsFactors = FALSE)
newbob_rest <- bob[!(sapply(bob, is.factor))]
newbob <- cbind(newbob_char, newbob_rest)

Không nên chuyển dữ liệu theo mã như thế này, tôi có thể làmsapply riêng phần đó (thực ra, làm như vậy dễ hơn nhiều), nhưng bạn hiểu rõ ... Tôi chưa kiểm tra mã, vì tôi Tôi không ở nhà, vì vậy tôi hy vọng nó hoạt động! =)

Tuy nhiên, cách tiếp cận này có một nhược điểm ... bạn phải sắp xếp lại các cột sau đó, trong khi với transformbạn có thể làm bất cứ điều gì bạn thích, nhưng với chi phí "viết mã theo kiểu người đi bộ" ...

Vậy nên ... =)


6

Ở đầu khung dữ liệu của bạn bao gồm stringsAsFactors = FALSEđể bỏ qua tất cả những hiểu lầm.


4

Nếu bạn sẽ sử dụng data.tablegói cho các hoạt động trên data.frame thì vấn đề không phải là hiện tại.

library(data.table)
dt = data.table(col1 = c("a","b","c"), col2 = 1:3)
sapply(dt, class)
#       col1        col2 
#"character"   "integer" 

Nếu bạn đã có một cột yếu tố trong tập dữ liệu của bạn và bạn muốn chuyển đổi chúng thành ký tự, bạn có thể làm như sau.

library(data.table)
dt = data.table(col1 = factor(c("a","b","c")), col2 = 1:3)
sapply(dt, class)
#     col1      col2 
# "factor" "integer" 
upd.cols = sapply(dt, is.factor)
dt[, names(dt)[upd.cols] := lapply(.SD, as.character), .SDcols = upd.cols]
sapply(dt, class)
#       col1        col2 
#"character"   "integer" 

DT cắt ngang sửa chữa sapply được đề xuất bởi Marek: In [<-.data.table(*tmp*, sapply(bob, is.factor), : Coerced 'character' RHS to 'double' to match the column's type. Either change the target column to 'character' first (by creating a new 'character' vector length 1234 (nrows of entire table) and assign that; i.e. 'replace' column), or coerce RHS to 'double' (e.g. 1L, NA_[real|integer]_, as.*, etc) to make your intent clear and for speed. Or, set the column type correctly up front when you create the table and stick to it, please.Dễ dàng hơn để sửa DF và tạo lại DT.
Matt Chambers

2

Điều này làm việc cho tôi - cuối cùng tôi đã tìm ra một lớp lót

df <- as.data.frame(lapply(df,function (y) if(class(y)=="factor" ) as.character(y) else y),stringsAsFactors=F)

2

Hàm này thực hiện thủ thuật

df <- stacomirtools::killfactor(df)

2

Có lẽ một lựa chọn mới hơn?

library("tidyverse")

bob <- bob %>% group_by_if(is.factor, as.character)

1

Bạn nên sử dụng converttrong hablarđó cung cấp cú pháp dễ đọc tương thích với các tidyverseđường ống:

library(dplyr)
library(hablar)

df <- tibble(a = factor(c(1, 2, 3, 4)),
             b = factor(c(5, 6, 7, 8)))

df %>% convert(chr(a:b))

cung cấp cho bạn:

  a     b    
  <chr> <chr>
1 1     5    
2 2     6    
3 3     7    
4 4     8   

1

Với việc dplyrsử dụng gói-package

bob=bob%>%mutate_at("phenotype", as.character)

nếu bạn chỉ muốn thay đổi phenotypecụ thể -column.


0

Công việc này chuyển đổi tất cả thành ký tự và sau đó là số thành số:

makenumcols<-function(df){
  df<-as.data.frame(df)
  df[] <- lapply(df, as.character)
  cond <- apply(df, 2, function(x) {
    x <- x[!is.na(x)]
    all(suppressWarnings(!is.na(as.numeric(x))))
  })
  numeric_cols <- names(df)[cond]
  df[,numeric_cols] <- sapply(df[,numeric_cols], as.numeric)
  return(df)
}

Chuyển thể từ: Nhận các loại cột của bảng excel tự động

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.