แปลงคอลัมน์ data.frame จากปัจจัยเป็นอักขระ


352

ฉันมีกรอบข้อมูล มาเรียกเขาว่า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-

ฉันต้องการต่อแถวของกรอบข้อมูลนี้ (นี่จะเป็นคำถามอื่น) แต่ดู:

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

Bobคอลัมน์เป็นปัจจัย ตัวอย่างเช่น:

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

ฉันไม่ได้เริ่มเข้าใจสิ่งนี้ แต่ฉันเดาว่าสิ่งเหล่านี้เป็นดัชนีในระดับของปัจจัยของคอลัมน์ (ของศาลของกษัตริย์ caractacus) ของbob? ไม่ใช่สิ่งที่ฉันต้องการ

น่าแปลกที่ฉันสามารถผ่านเสาbobด้วยมือและทำ

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

ซึ่งใช้งานได้ดี และหลังจากพิมพ์ฉันจะได้รับ data.frame ซึ่งคอลัมน์เป็นอักขระมากกว่าปัจจัย ดังนั้นคำถามของฉันคือฉันจะทำสิ่งนี้โดยอัตโนมัติได้อย่างไร ฉันจะแปลง data.frame ด้วยคอลัมน์ factor เป็น data.frame ด้วยคอลัมน์อักขระได้โดยไม่ต้องไปแต่ละคอลัมน์ด้วยตนเองได้อย่างไร

คำถามโบนัส: ทำไมวิธีการแบบแมนนวลถึงทำงานอย่างไร


3
bobคงจะดีถ้าคุณจะทำให้ทำซ้ำคำถามเพื่อให้รวมถึงโครงสร้างของ
jangorecki

คำตอบ:


362

เพียงติดตามที่ Matt และ Dirk หากคุณต้องการสร้างกรอบข้อมูลที่มีอยู่โดยไม่ต้องเปลี่ยนตัวเลือกทั่วโลกคุณสามารถสร้างกรอบข้อมูลใหม่ได้โดยใช้คำสั่ง Apply:

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

นี้จะแปลงตัวแปรทั้งหมดในชั้นเรียน "ตัวอักษร" ถ้าคุณต้องการปัจจัยแปลงเท่านั้นดูวิธีการแก้ปัญหามาเร็คด้านล่าง

@hadley ชี้ให้เห็นดังต่อไปนี้จะกระชับมากขึ้น

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

ในทั้งสองกรณีlapplyเอาต์พุตรายการ แต่เนื่องจากคุณสมบัติวิเศษของ R, การใช้[]ในกรณีที่สองช่วยให้ระดับ data.frame ของbobวัตถุจึงไม่จำเป็นต้องแปลงกลับไป data.frame ใช้กับการโต้แย้งas.data.framestringsAsFactors = FALSE


27
Shane ซึ่งจะเปลี่ยนคอลัมน์ตัวเลขให้เป็นอักขระ
Dirk Eddelbuettel

@ เดิร์ก: มันเป็นความจริงแม้ว่ามันจะไม่ชัดเจนว่าเป็นปัญหาที่นี่ เห็นได้ชัดว่าการสร้างสิ่งต่าง ๆ อย่างถูกต้องเป็นทางออกที่ดีที่สุด ฉันไม่คิดว่ามันเป็นเรื่องง่ายในการแปลงชนิดข้อมูลโดยอัตโนมัติในกรอบข้อมูล ตัวเลือกหนึ่งคือการใช้ดังกล่าวข้างต้น แต่แล้วใช้type.convertหลังจากหล่อทุกอย่างcharacterแล้วหลอมfactorsกลับไปcharacterอีกครั้ง
เชน

ดูเหมือนว่าจะยกเลิกชื่อแถว
piccolbo

2
@piccolbo คุณใช้bob[] <- ในตัวอย่างหรือbob <- ?; อันแรกเก็บ data.frame; ครั้งที่สองเปลี่ยน data.frame เป็นรายการโดยปล่อยชื่อแถว ฉันจะอัปเดตคำตอบ
David LeBauer

6
ตัวแปรที่แปลงคอลัมน์ปัจจัยเป็นอักขระโดยใช้ฟังก์ชันที่ไม่ระบุตัวตนเท่านั้น: iris[] <- lapply(iris, function(x) if (is.factor(x)) as.character(x) else {x})
Stefan F

313

หากต้องการแทนที่ปัจจัยเท่านั้น:

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

ในแพ็คเกจ dplyr ในฟังก์ชั่นใหม่เวอร์ชั่น 0.5.0 mutate_ifได้รับการแนะนำ :

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

แพ็คเกจ purrr จาก RStudioให้ทางเลือกอื่น:

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

ไม่ทำงานสำหรับฉันเศร้า ไม่รู้ทำไม อาจเป็นเพราะฉันมีชื่อเล่นไหม
Autumnsault

@mohawkjohn ไม่น่าจะมีปัญหา คุณมีข้อผิดพลาดหรือผลลัพธ์ไม่เป็นไปตามที่คาดไว้?
Marek

2
หมายเหตุ: purrrบรรทัดส่งคืนรายการไม่ใช่data.frame!
RoyalTS

นอกจากนี้ยังทำงานถ้าคุณมีอยู่แล้วว่าเป็นเวกเตอร์ของi colnames()
verbamour

39

ตัวเลือกระดับโลก

stringsAsFactors: การตั้งค่าเริ่มต้นสำหรับอาร์กิวเมนต์ของ data.frame และ read.table

อาจเป็นสิ่งที่คุณต้องการตั้งค่าFALSEในไฟล์เริ่มต้นของคุณ (เช่น ~ / .Rprofile) help(options)โปรดดู


5
ปัญหานี้คือเมื่อคุณรันโค้ดของคุณในสภาพแวดล้อมที่ไฟล์. profile หายไปคุณจะได้รับบั๊ก!
waferthin

4
ฉันมักจะเรียกมันว่าจุดเริ่มต้นของสคริปต์มากกว่าการตั้งค่าอยู่ใน. โปรไฟล์
gregmacfarlane

22

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

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

> 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

ตัวเลขที่ส่งคืนในบรรทัดสุดท้ายสอดคล้องกับระดับของปัจจัย

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

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

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

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

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

20

หากคุณต้องการกรอบข้อมูลใหม่bobcที่ทุกเวกเตอร์ในปัจจัยbobfถูกแปลงเป็นเวกเตอร์อักขระลองนี้:

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

หากคุณต้องการแปลงกลับคุณสามารถสร้างเวกเตอร์แบบลอจิคัลซึ่งคอลัมน์เป็นปัจจัยและใช้เพื่อเลือกปัจจัยที่มีผล

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

2
+1 สำหรับทำสิ่งที่จำเป็นเท่านั้น (เช่นไม่แปลง data.frame ทั้งหมดให้เป็นอักขระ) โซลูชันนี้แข็งแกร่งสำหรับ data.frame ที่มีชนิดแบบผสม
Joshua Ulrich

3
ตัวอย่างเช่นนี้ควรจะเป็นในส่วนของ `ตัวอย่างสำหรับ rapply เช่นที่: stat.ethz.ch/R-manual/R-devel/library/base/html/rapply.html ใครรู้วิธีที่จะขอให้เป็นเช่นนั้น?
mpettis

ถ้าคุณต้องการที่จะจบลงด้วย data frame ให้ทำการแรปอย่างง่ายในการเรียก data.frame (โดยใช้ stringsAsFactors ที่ตั้งค่าเป็นอาร์กิวเมนต์ FALSE)
Taylored Web Sites

13

ฉันมักจะทำให้ฟังก์ชั่นนี้แตกต่างจากโครงการทั้งหมดของฉัน ง่ายและรวดเร็ว

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

8

อีกวิธีคือการแปลงโดยใช้

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

และอันที่ดีกว่า (ก่อนหน้านี้คือคลาส 'เมทริกซ์')

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

กำลังติดตามความคิดเห็นของ @ Shane: เพื่อรับ data.frame ให้ทำas.data.frame(lapply(...
aL3xa

7

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

ลองสิ่งนี้:

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

โดยทั่วไปเมื่อใดก็ตามที่คุณมีปัญหาเกี่ยวกับปัจจัยที่ควรเป็นตัวละครมีการstringsAsFactorsตั้งค่าบางอย่างเพื่อช่วยคุณ (รวมถึงการตั้งค่าระดับโลก)


1
สิ่งนี้ใช้ได้ผลถ้าเขาตั้งไว้เมื่อสร้างbobเพื่อเริ่มต้นด้วย (แต่ไม่ใช่หลังความจริง)
เชน

ขวา. เพียงแค่ต้องการที่จะชัดเจนว่าสิ่งนี้ไม่ได้แก้ปัญหาต่อ แต่ - ขอบคุณที่สังเกตว่ามันจะป้องกันไม่ให้
Matt Parker

7

หรือคุณสามารถลองtransform:

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

ให้แน่ใจว่าได้ใส่ทุกปัจจัยที่คุณต้องการแปลงเป็นตัวละคร

หรือคุณสามารถทำสิ่งนี้และฆ่าศัตรูด้วยการเป่า:

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)

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

อย่างไรก็ตามวิธีนี้มีข้อเสีย ... คุณต้องจัดเรียงคอลัมน์ใหม่หลังจากนั้นในขณะที่transformคุณสามารถทำอะไรก็ได้ที่คุณชอบ แต่ด้วยราคาของ"pedestrian-style-code-writting" ...

ดังนั้นจึงมี ... =)


6

ที่จุดเริ่มต้นของเฟรมข้อมูลของคุณรวมถึงstringsAsFactors = FALSEการละเว้นความเข้าใจผิดทั้งหมด


4

หากคุณต้องการใช้data.tableแพ็กเกจสำหรับการดำเนินการกับ data.frame ปัญหาจะไม่ปรากฏ

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

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

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 หลีกเลี่ยงการแก้ไขอย่างสุภาพที่เสนอโดย 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.มันง่ายกว่าที่จะแก้ไข DF และสร้าง DT ใหม่
Matt Chambers

2

สิ่งนี้เหมาะกับฉัน - ในที่สุดฉันก็คิดว่าเป็นหนึ่งในสายการบิน

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



1

คุณควรใช้convertในhablarซึ่งจะช่วยให้สามารถอ่านได้ไวยากรณ์เข้ากันได้กับtidyverseท่อ:

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

ซึ่งให้คุณ:

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

1

ด้วยdplyr-package โหลดใช้งาน

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

หากคุณต้องการเปลี่ยนphenotypeเฉพาะคอลัมน์ -column เท่านั้น


0

งานนี้เปลี่ยนเป็นอักขระทั้งหมดจากนั้นให้เป็นตัวเลข:

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)
}

ดัดแปลงจาก: รับประเภทคอลัมน์ของ excel ชีตโดยอัตโนมัติ

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