จะอ่านข้อมูลได้อย่างไรเมื่อตัวเลขบางตัวมีเครื่องหมายจุลภาคเป็นตัวคั่นหลักพัน


117

ฉันมีไฟล์ csv ที่ค่าตัวเลขบางส่วนแสดงเป็นสตริงที่มีเครื่องหมายจุลภาคเป็นตัวคั่นหลักพันเช่น"1,513"แทนที่จะเป็น1513ไฟล์. วิธีที่ง่ายที่สุดในการอ่านข้อมูลเป็น R คืออะไร?

ฉันสามารถใช้ได้read.csv(..., colClasses="character")แต่ฉันต้องตัดเครื่องหมายจุลภาคออกจากองค์ประกอบที่เกี่ยวข้องก่อนที่จะแปลงคอลัมน์เหล่านั้นเป็นตัวเลขและฉันไม่พบวิธีที่เป็นระเบียบในการทำเช่นนั้น

คำตอบ:


141

ไม่แน่ใจว่าจะread.csvตีความอย่างไรให้ถูกต้อง แต่คุณสามารถใช้gsubแทนที่","ด้วย""แล้วแปลงสตริงเป็นnumericโดยใช้as.numeric:

y <- c("1,200","20,000","100","12,111")
as.numeric(gsub(",", "", y))
# [1]  1200 20000 100 12111

สิ่งนี้ได้รับคำตอบก่อนหน้านี้ใน R-Help (และในQ2 ที่นี่ )

หรือคุณสามารถประมวลผลไฟล์ล่วงหน้าได้เช่นsedใน unix


60

คุณสามารถให้ read.table หรือ read.csv ทำการแปลงนี้ให้คุณแบบกึ่งอัตโนมัติ ขั้นแรกให้สร้างนิยามคลาสใหม่จากนั้นสร้างฟังก์ชันการแปลงและตั้งเป็นวิธีการ "เป็น" โดยใช้ฟังก์ชัน setAs ดังนี้:

setClass("num.with.commas")
setAs("character", "num.with.commas", 
        function(from) as.numeric(gsub(",", "", from) ) )

จากนั้นเรียกใช้ read.csv เช่น:

DF <- read.csv('your.file.here', 
   colClasses=c('num.with.commas','factor','character','numeric','num.with.commas'))

3
นี่เป็นเคล็ดลับที่ดีมาก สามารถใช้สำหรับการแปลงเมื่อนำเข้า (ตัวอย่างเช่นการแปลงค่า Y / N เป็นเวกเตอร์ตรรกะโดยใช้setAs("character", "logical.Y.N", function(from) c(Y=TRUE,N=FALSE)[from] ))
Marek

1
เคล็ดลับการใช้เหมือนกันในปัญหาที่คล้ายกัน และเพื่อเพิ่ม: สามารถใช้อย่างใดอย่างหนึ่งsetClass("num.with.commas")หรือsuppresMessage(setAs(.....))เพื่อหลีกเลี่ยงข้อความเกี่ยวกับคลาสที่หายไป
Marek

สวัสดี Greg ขอบคุณสำหรับการแบ่งปันฟังก์ชันที่มีประโยชน์นี้ เมื่อดำเนินการฉันได้รับคำเตือนต่อไปนี้: ในวิธีการ 'coerce' ด้วยลายเซ็น '"character", "num.with.commas"': ไม่มีคำจำกัดความสำหรับคลาส“ num.with.commas” ความคิดใด ๆ ที่เป็นปัญหาที่นี่ ฉันมีคำรหัสของคุณสำหรับ word?
TheGoat

ฉันตรวจสอบลิงก์ปัญหาที่คล้ายกันและเห็นว่าฉันต้องตั้งค่าชั้นเรียน! ขอบคุณสำหรับเคล็ดลับเรียบร้อย
TheGoat

17

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

x <- read.csv("file.csv",header=TRUE,colClasses="character")
col2cvt <- 15:41
x[,col2cvt] <- lapply(x[,col2cvt],function(x){as.numeric(gsub(",", "", x))})

colClasses = "char" ไม่บังคับให้คอลัมน์ทั้งหมดเป็น char ในกรณีอื่น ๆ นอกเหนือจาก 15:41 จะเป็น char ด้วยหรือไม่ บางทีการปล่อยให้ read.csv () ตัดสินใจแล้วแปลงค่าที่อยู่ใน cols 15:41 อาจทำให้คุณได้รับคอลัมน์ตัวเลข 'มากกว่า'
Dirk Eddelbuettel

ใช่ แต่ตามคำถามของฉันคอลัมน์อื่น ๆ ทั้งหมดเป็นอักขระ ฉันสามารถใช้ as.is = TRUE แทนซึ่งจะกว้างกว่า แต่การปล่อยให้ read.csv () ตัดสินใจโดยใช้อาร์กิวเมนต์เริ่มต้นนั้นไม่เป็นประโยชน์เพราะจะแปลงอะไรก็ตามที่ดูเหมือนอักขระให้เป็นปัจจัยที่ทำให้เกิดความยุ่งยากสำหรับคอลัมน์ตัวเลขดังนั้นจึงไม่สามารถแปลงได้อย่างถูกต้องโดยใช้ as.numeric () .
Rob Hyndman

คุณควรพิจารณาตั้งค่าอาร์กิวเมนต์ dec = ในตารางอ่านเป็น "." นั่นเป็นค่าดีฟอลต์สำหรับ read.csv2 แต่มีการเดินสายคอมมาเป็น read.csv ()
IRTFM

15

คำถามนี้มีอายุหลายปีแล้ว แต่ฉันก็สะดุดกับคำถามนี้ซึ่งหมายความว่าคนอื่นอาจจะ

readrห้องสมุด / แพคเกจที่มีคุณสมบัติที่ดีบางอย่างกับมัน หนึ่งในนั้นเป็นวิธีที่ดีในการตีความคอลัมน์ "ยุ่ง" เช่นนี้

library(readr)
read_csv("numbers\n800\n\"1,800\"\n\"3500\"\n6.5",
          col_types = list(col_numeric())
        )

สิ่งนี้ให้ผลตอบแทน

ที่มา: local data frame [4 x 1]

  numbers
    (dbl)
1   800.0
2  1800.0
3  3500.0
4     6.5

จุดสำคัญเมื่อมีการอ่านในรูปแบบไฟล์: คุณอาจจะต้องดำเนินการก่อนเช่นความคิดเห็นข้างต้นเกี่ยวกับsedหรือคุณมีที่จะดำเนินการในขณะที่การอ่าน บ่อยครั้งหากคุณพยายามแก้ไขสิ่งต่างๆตามความเป็นจริงมีข้อสันนิษฐานที่เป็นอันตรายบางอย่างที่หาได้ยาก (ซึ่งเป็นเหตุผลว่าทำไมไฟล์แบนจึงชั่วร้ายในตอนแรก)

ตัวอย่างเช่นหากฉันไม่ได้ตั้งค่าสถานะcol_typesฉันจะได้รับสิ่งนี้:

> read_csv("numbers\n800\n\"1,800\"\n\"3500\"\n6.5")
Source: local data frame [4 x 1]

  numbers
    (chr)
1     800
2   1,800
3    3500
4     6.5

(สังเกตว่าตอนนี้เป็นchr( character) แทนที่จะเป็น a numeric)

หรืออันตรายกว่านั้นถ้ามันยาวพอและองค์ประกอบแรก ๆ ส่วนใหญ่ไม่มีเครื่องหมายจุลภาค:

> set.seed(1)
> tmp <- as.character(sample(c(1:10), 100, replace=TRUE))
> tmp <- c(tmp, "1,003")
> tmp <- paste(tmp, collapse="\"\n\"")

(ดังนั้นองค์ประกอบบางส่วนสุดท้ายดูเหมือน :)

\"5\"\n\"9\"\n\"7\"\n\"1,003"

แล้วคุณจะพบปัญหาในการอ่านลูกน้ำนั้นเลย!

> tail(read_csv(tmp))
Source: local data frame [6 x 1]

     3"
  (dbl)
1 8.000
2 5.000
3 5.000
4 9.000
5 7.000
6 1.003
Warning message:
1 problems parsing literal data. See problems(...) for more details. 

7

dplyrวิธีการแก้ปัญหาโดยใช้mutate_allและท่อ

บอกว่าคุณมีสิ่งต่อไปนี้:

> dft
Source: local data frame [11 x 5]

   Bureau.Name Account.Code   X2014   X2015   X2016
1       Senate          110 158,000 211,000 186,000
2       Senate          115       0       0       0
3       Senate          123  15,000  71,000  21,000
4       Senate          126   6,000  14,000   8,000
5       Senate          127 110,000 234,000 134,000
6       Senate          128 120,000 159,000 134,000
7       Senate          129       0       0       0
8       Senate          130 368,000 465,000 441,000
9       Senate          132       0       0       0
10      Senate          140       0       0       0
11      Senate          140       0       0       0

และต้องการลบเครื่องหมายจุลภาคออกจากตัวแปรปี X2014-X2016 และแปลงเป็นตัวเลข สมมติว่า X2014-X2016 อ่านเป็นปัจจัย (ค่าเริ่มต้น)

dft %>%
    mutate_all(funs(as.character(.)), X2014:X2016) %>%
    mutate_all(funs(gsub(",", "", .)), X2014:X2016) %>%
    mutate_all(funs(as.numeric(.)), X2014:X2016)

mutate_allใช้ฟังก์ชันภายในfunsกับคอลัมน์ที่ระบุ

ฉันทำตามลำดับทีละฟังก์ชั่น (ถ้าคุณใช้หลายฟังก์ชั่นภายในfunsคุณจะสร้างคอลัมน์เพิ่มเติมที่ไม่จำเป็น)


3
mutate_eachเลิกใช้แล้ว คุณต้องการอัปเดตคำตอบของคุณด้วยmutate_atหรือคล้ายกัน
T_T

6

"Preprocess" ใน R:

lines <- "www, rrr, 1,234, ttt \n rrr,zzz, 1,234,567,987, rrr"

สามารถใช้readLinesกับไฟล์textConnection. จากนั้นลบเฉพาะเครื่องหมายจุลภาคที่อยู่ระหว่างหลัก:

gsub("([0-9]+)\\,([0-9])", "\\1\\2", lines)

## [1] "www, rrr, 1234, ttt \n rrr,zzz, 1234567987, rrr"

เป็นประโยชน์ที่จะทราบ แต่ไม่เกี่ยวข้องโดยตรงกับคำถามนี้ว่าเครื่องหมายจุลภาคเป็นตัวคั่นทศนิยมสามารถจัดการได้โดย read.csv2 (อัตโนมัติ) หรือ read.table (ด้วยการตั้งค่าพารามิเตอร์ 'dec'-)

แก้ไข: ต่อมาฉันค้นพบวิธีใช้ colClasses โดยการออกแบบคลาสใหม่ ดู:

จะโหลด df ด้วยตัวคั่น 1000 ใน R เป็นคลาสตัวเลขได้อย่างไร


ขอบคุณนี่เป็นตัวชี้ที่ดี แต่ใช้ไม่ได้กับตัวเลขที่มีเครื่องหมายทศนิยมหลายตัวเช่น 1,234,567.89 ซึ่งจำเป็นในการแก้ไขปัญหานี้เพื่อนำเข้า Google สเปรดชีตไปยัง R โปรดดูstackoverflow.com/a/30020171/3096626สำหรับวิธีง่ายๆ ฟังก์ชันที่ทำงานสำหรับเครื่องหมายทศนิยมหลายรายการ
flexponsive

4

ถ้าตัวเลขคั่นด้วย "." และทศนิยมโดย "," (1.200.000,00) ในการโทรgsubคุณต้องset fixed=TRUE as.numeric(gsub(".","",y,fixed=TRUE))


3

วิธีที่สะดวกมากคือreadr::read_delim- ครอบครัว นำตัวอย่างจากที่นี่: การนำเข้า csv ด้วยตัวคั่นหลายตัวไปยัง Rคุณสามารถทำได้ดังนี้:

txt <- 'OBJECTID,District_N,ZONE_CODE,COUNT,AREA,SUM
1,Bagamoyo,1,"136,227","8,514,187,500.000000000000000","352,678.813105723350000"
2,Bariadi,2,"88,350","5,521,875,000.000000000000000","526,307.288878142830000"
3,Chunya,3,"483,059","30,191,187,500.000000000000000","352,444.699742995200000"'

require(readr)
read_csv(txt) # = read_delim(txt, delim = ",")

ซึ่งผลลัพธ์ที่คาดหวัง:

# A tibble: 3 × 6
  OBJECTID District_N ZONE_CODE  COUNT        AREA      SUM
     <int>      <chr>     <int>  <dbl>       <dbl>    <dbl>
1        1   Bagamoyo         1 136227  8514187500 352678.8
2        2    Bariadi         2  88350  5521875000 526307.3
3        3     Chunya         3 483059 30191187500 352444.7

3

การใช้ฟังก์ชัน read_delim ซึ่งเป็นส่วนหนึ่งของไลบรารีreadrคุณสามารถระบุพารามิเตอร์เพิ่มเติม:

locale = locale(decimal_mark = ",")

read_delim("filetoread.csv", ';", locale = locale(decimal_mark = ","))

* อัฒภาคในบรรทัดที่สองหมายความว่า read_delim จะอ่านค่าที่คั่นด้วยอัฒภาค csv

วิธีนี้จะช่วยในการอ่านตัวเลขทั้งหมดโดยใช้ลูกน้ำเป็นตัวเลขที่เหมาะสม

ความนับถือ

Mateusz Kania


3

นอกจากนี้เรายังสามารถใช้readr::parse_numberคอลัมน์ต้องเป็นอักขระ หากเราต้องการใช้กับหลายคอลัมน์เราสามารถวนซ้ำคอลัมน์โดยใช้lapply

df[2:3] <- lapply(df[2:3], readr::parse_number)
df

#  a        b        c
#1 a    12234       12
#2 b      123  1234123
#3 c     1234     1234
#4 d 13456234    15342
#5 e    12312 12334512

หรือใช้mutate_atจากdplyrเพื่อนำไปใช้กับตัวแปรเฉพาะ

library(dplyr)
df %>% mutate_at(2:3, readr::parse_number)
#Or
df %>% mutate_at(vars(b:c), readr::parse_number)

ข้อมูล

df <- data.frame(a = letters[1:5], 
                 b = c("12,234", "123", "1,234", "13,456,234", "123,12"),
                 c = c("12", "1,234,123","1234", "15,342", "123,345,12"), 
                 stringsAsFactors = FALSE)

1

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

ตัวอย่างเช่นหากไฟล์ของคุณเป็นแบบนี้:

"1,234","123","1,234"
"234","123","1,234"
123,456,789

จากนั้นคุณสามารถใช้นิพจน์ทั่วไป"([0-9]+),([0-9]+)"และแทนที่ด้วย\1\2

1234,"123",1234
"234","123",1234
123,456,789

จากนั้นคุณสามารถใช้ x <- read.csv(file="x.csv",header=FALSE)เพื่ออ่านไฟล์


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