แปลงคอลัมน์ 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.