Cắt một tệp csv khổng lồ (3,5 GB) để đọc thành R


87

Vì vậy, tôi có một tệp dữ liệu (được phân tách bằng dấu chấm phẩy) có rất nhiều hàng chi tiết và không đầy đủ (dẫn đến Access và SQL bị nghẹt thở). Đó là tập dữ liệu cấp hạt được chia thành các phân đoạn, phân đoạn con và phân đoạn phụ (với tổng số ~ 200 yếu tố) trong 40 năm. Tóm lại, nó rất lớn, và nó sẽ không vừa với bộ nhớ nếu tôi cố gắng đọc nó một cách đơn giản.

Vì vậy, câu hỏi của tôi là thế này, với điều kiện tôi muốn tất cả các quận, nhưng chỉ một năm duy nhất (và chỉ là mức cao nhất của phân khúc ... cuối cùng dẫn đến khoảng 100.000 hàng), cách tốt nhất để tiếp tục cuộn lên này thành R?

Hiện tại, tôi đang cố gắng loại bỏ những năm không liên quan với Python, vượt qua giới hạn kích thước tệp bằng cách đọc và thao tác trên một dòng tại một thời điểm, nhưng tôi thích giải pháp chỉ R (gói CRAN được). Có cách nào tương tự để đọc từng đoạn trong tệp trong R không?

bất kì ý kiến ​​nào đều được đánh giá cao.

Cập nhật:

  • Ràng buộc
    • Cần sử dụng máy của tôi , vì vậy không có phiên bản EC2 nào
    • Chỉ R càng tốt. Tốc độ và tài nguyên không phải là mối quan tâm trong trường hợp này ... miễn là máy của tôi không phát nổ ...
    • Như bạn có thể thấy bên dưới, dữ liệu chứa các loại hỗn hợp, mà tôi cần thao tác sau
  • Dữ liệu
    • Dữ liệu là 3,5GB, với khoảng 8,5 triệu hàng và 17 cột
    • Vài nghìn hàng (~ 2k) không đúng định dạng, chỉ có một cột thay vì 17
      • Những thứ này hoàn toàn không quan trọng và có thể bị bỏ
    • Tôi chỉ cần ~ 100.000 hàng từ tệp này (Xem bên dưới)

Ví dụ dữ liệu:

County; State; Year; Quarter; Segment; Sub-Segment; Sub-Sub-Segment; GDP; ...
Ada County;NC;2009;4;FIRE;Financial;Banks;80.1; ...
Ada County;NC;2010;1;FIRE;Financial;Banks;82.5; ...
NC  [Malformed row]
[8.5 Mill rows]

Tôi muốn cắt nhỏ một số cột và chọn hai trong số 40 năm khả dụng (2009-2010 từ 1980-2020), để dữ liệu có thể vừa với R:

County; State; Year; Quarter; Segment; GDP; ...
Ada County;NC;2009;4;FIRE;80.1; ...
Ada County;NC;2010;1;FIRE;82.5; ...
[~200,000 rows]

Các kết quả:

Sau khi tìm hiểu tất cả các đề xuất được đưa ra, tôi quyết định rằng readLines, do JD và Marek đề xuất, sẽ hoạt động tốt nhất. Tôi đưa cho Marek tấm séc vì anh ấy đã đưa ra một bản triển khai mẫu.

Tôi đã sao chép một phiên bản được điều chỉnh một chút của việc triển khai Marek cho câu trả lời cuối cùng của tôi ở đây, sử dụng strsplit và cat để chỉ giữ lại các cột tôi muốn.

Cũng cần phải nhấn mạnh rằng đây là NHIÊU kém hiệu quả hơn so với Python ... như trong, Python chomps thông qua file 3.5GB trong vòng 5 phút trong khi R mất khoảng 60 ... nhưng nếu tất cả các bạn có là R thì đây là vé.

## Open a connection separately to hold the cursor position
file.in <- file('bad_data.txt', 'rt')
file.out <- file('chopped_data.txt', 'wt')
line <- readLines(file.in, n=1)
line.split <- strsplit(line, ';')
# Stitching together only the columns we want
cat(line.split[[1]][1:5], line.split[[1]][8], sep = ';', file = file.out, fill = TRUE)
## Use a loop to read in the rest of the lines
line <- readLines(file.in, n=1)
while (length(line)) {
  line.split <- strsplit(line, ';')
  if (length(line.split[[1]]) > 1) {
    if (line.split[[1]][3] == '2009') {
        cat(line.split[[1]][1:5], line.split[[1]][8], sep = ';', file = file.out, fill = TRUE)
    }
  }
  line<- readLines(file.in, n=1)
}
close(file.in)
close(file.out)

Thất bại theo Cách tiếp cận:

  • sqldf
    • Đây chắc chắn là những gì tôi sẽ sử dụng cho loại vấn đề này trong tương lai nếu dữ liệu được định hình tốt. Tuy nhiên, nếu không, thì SQLite bị nghẹt.
  • MapReduce
    • Thành thật mà nói, các tài liệu đã đe dọa tôi về điều này một chút, vì vậy tôi đã không cố gắng thử nó. Có vẻ như nó yêu cầu đối tượng cũng phải ở trong bộ nhớ, điều này sẽ đánh bại điểm nếu đúng như vậy.
  • bigmemory
    • Cách tiếp cận này được liên kết rõ ràng với dữ liệu, nhưng nó chỉ có thể xử lý một loại tại một thời điểm. Kết quả là, tất cả các vectơ ký tự của tôi đều giảm khi được đưa vào big.table. Tuy nhiên, nếu tôi cần thiết kế các tập dữ liệu lớn cho tương lai, tôi chỉ cân nhắc sử dụng các con số để duy trì tùy chọn này.
  • quét
    • Quét dường như có các vấn đề loại tương tự như bộ nhớ lớn, nhưng với tất cả các cơ chế của readLines. Tóm lại, nó không phù hợp với dự luật lần này.

3
Nếu tiêu chí của bạn đủ đơn giản, bạn có thể sử dụng sedvà / hoặc awktạo một phiên bản CSV cắt nhỏ mà bạn có thể đọc trực tiếp. Vì đây là một cách giải quyết hơn là một câu trả lời, tôi sẽ để nó dưới dạng một nhận xét.
Hank Gay

Tôi đồng ý với Hank - bạn nên sử dụng đúng công cụ cho công việc, và nếu đó là dữ liệu đơn giản làm sạch / loại bỏ hàng không liên quan / cột dòng lệnh công cụ dòng thích loại / sed / awk là tuyệt vời và sẽ được cách ít nguồn lực chuyên sâu hơn R hoặc python - nếu bạn đưa ra một mẫu của tập tin của bạn định dạng chúng ta có thể có thể đưa ra một ví dụ
Aaron Statham

Tuyệt quá. Hãy cho chúng tôi biết những gì bạn khám phá.
Shane

@Hank & Aaron: Tôi thường sử dụng công cụ phù hợp cho công việc, nhưng với điều này là trên máy Windows tại nơi làm việc và tôi đang học R khi đang làm, tôi nghĩ rằng đó sẽ là một bài tập tốt trước các phương pháp hay nhất và hãy thử ở dạng R-only nếu có thể.
FTWynn

2
Để tham khảo trong tương lai, hãy xem gói data.table R. Các freadchức năng nhanh hơn nhiều read.table. Sử dụng một cái gì đó như x = fread(file_path_here, data.table=FALSE)để tải nó vào như một data.frameđối tượng.
palo13

Câu trả lời:


39

Tôi thử với readLines. Đoạn mã này tạo csvvới các năm đã chọn.

file_in <- file("in.csv","r")
file_out <- file("out.csv","a")
x <- readLines(file_in, n=1)
writeLines(x, file_out) # copy headers

B <- 300000 # depends how large is one pack
while(length(x)) {
    ind <- grep("^[^;]*;[^;]*; 20(09|10)", x)
    if (length(ind)) writeLines(x[ind], file_out)
    x <- readLines(file_in, n=B)
}
close(file_in)
close(file_out)

Đây gần như là chính xác những gì tôi vừa viết. Tôi cảm thấy đây cũng sẽ là câu trả lời tốt nhất, với các hạn chế về bộ nhớ, các loại hỗn hợp và các hàng không đúng định dạng.
FTWynn

10

Tôi không phải là chuyên gia về vấn đề này, nhưng bạn có thể cân nhắc việc thử MapReduce , về cơ bản có nghĩa là thực hiện phương pháp "chia để trị". R có một số tùy chọn cho việc này, bao gồm:

  1. mapReduce (thuần R)
  2. RHIPE (sử dụng Hadoop ); xem ví dụ 6.2.2 trong tài liệu để biết ví dụ về tập hợp con tệp

Ngoài ra, R cung cấp một số gói để xử lý dữ liệu lớn đi ra ngoài bộ nhớ (trên đĩa). Bạn có thể tải toàn bộ tập dữ liệu vào một bigmemoryđối tượng và thực hiện việc giảm hoàn toàn trong R. Xem http://www.bigmemory.org/ để biết bộ công cụ xử lý điều này.


Đề xuất tốt, nhưng tôi không có nhiều kinh nghiệm với MapReduce và ilk của nó. Tôi sẽ phải đọc nó.
FTWynn

bigmemorycó thể dễ dàng hơn để bạn thử trước, trong trường hợp đó.
Shane

10

Có cách nào tương tự để đọc từng đoạn trong tệp trong R không?

Đúng. Hàm readChar () sẽ đọc trong một khối ký tự mà không giả sử chúng được kết thúc bằng null. Nếu bạn muốn đọc dữ liệu trong một dòng tại một thời điểm, bạn có thể sử dụng readLines () . Nếu bạn đọc một khối hoặc một dòng, thực hiện một thao tác, sau đó ghi dữ liệu ra ngoài, bạn có thể tránh được sự cố bộ nhớ. Mặc dù nếu bạn muốn kích hoạt phiên bản bộ nhớ lớn trên EC2 của Amazon, bạn có thể nhận được tối đa 64GB RAM. Điều đó sẽ chứa tệp của bạn cộng với nhiều chỗ để thao tác dữ liệu.

Nếu bạn cần nhiều tốc độ hơn, thì khuyến nghị của Shane sử dụng Map Reduce là rất tốt. Tuy nhiên, nếu bạn sử dụng phiên bản bộ nhớ lớn trên EC2, bạn nên xem gói đa lõi để sử dụng tất cả các lõi trên máy.

Nếu bạn thấy mình muốn đọc nhiều hợp đồng biểu diễn dữ liệu được phân tách trong R, ít nhất bạn nên nghiên cứu gói sqldf cho phép bạn nhập trực tiếp vào sqldf từ R và sau đó thao tác trên dữ liệu từ bên trong R. Tôi thấy sqldf là một các cách nhanh nhất để nhập dữ liệu biểu diễn vào R, như đã đề cập trong câu hỏi trước .


Tôi sẽ lưu ý đến phiên bản EC2, nhưng hiện tại tôi phải gắn vào máy tính để bàn của mình và đó là RAM 2GB. sqldf chắc chắn có vẻ giống như những gì tôi đã nghĩ đến. Tuy nhiên, nó cũng làm nghẹt các hàng không đúng định dạng (Nên có 17 cột, nhưng vài nghìn hàng chỉ có một). Điều đó có gọi cho một số phương pháp tiền xử lý khác không hay có tùy chọn nào tôi đang thiếu không?
FTWynn

6

Có một gói hoàn toàn mới có tên là colbycol cho phép bạn chỉ đọc các biến mà bạn muốn từ các tệp văn bản khổng lồ:

http://colbycol.r-forge.r-project.org/

Nó chuyển bất kỳ đối số nào cùng với read.table, vì vậy sự kết hợp sẽ cho phép bạn tập hợp con khá chặt chẽ.


6

Các ffgói là một cách minh bạch để đối phó với các tập tin lớn.

Bạn có thể xem trang web gói và / hoặc bản trình bày về nó.

Tôi hi vọng cái này giúp được


5

Bạn có thể nhập dữ liệu vào cơ sở dữ liệu SQLite và sau đó sử dụng RSQLite để chọn các tập hợp con.


Một kế hoạch tốt, nhưng vì đây thực chất là những gì sqldf thực hiện đằng sau hậu trường, tôi thích điều đó hơn. Trừ khi có cách tốt hơn để xử lý các hàng không đúng định dạng nếu bạn sử dụng RSQLite thẳng?
FTWynn

5

Còn việc sử dụng readrread_*_chunkedgia đình?

Vì vậy, trong trường hợp của bạn:

testfile.csv

County; State; Year; Quarter; Segment; Sub-Segment; Sub-Sub-Segment; GDP
Ada County;NC;2009;4;FIRE;Financial;Banks;80.1
Ada County;NC;2010;1;FIRE;Financial;Banks;82.5
lol
Ada County;NC;2013;1;FIRE;Financial;Banks;82.5

Mã thực tế

require(readr)
f <- function(x, pos) subset(x, Year %in% c(2009, 2010))
read_csv2_chunked("testfile.csv", DataFrameCallback$new(f), chunk_size = 1)

Điều này áp dụng fcho từng đoạn, ghi nhớ tên col và kết hợp các kết quả đã lọc cuối cùng. Hãy xem đâu ?callbacklà nguồn của ví dụ này.

Kết quả này trong:

# A tibble: 2 × 8
      County State  Year Quarter Segment `Sub-Segment` `Sub-Sub-Segment`   GDP
*      <chr> <chr> <int>   <int>   <chr>         <chr>             <chr> <dbl>
1 Ada County    NC  2009       4    FIRE     Financial             Banks   801
2 Ada County    NC  2010       1    FIRE     Financial             Banks   825

Bạn thậm chí có thể tăng chunk_sizenhưng trong ví dụ này chỉ có 4 dòng.



3

Có lẽ bạn có thể di chuyển sang MySQL hoặc PostgreSQL để tránh các hạn chế của MS Access.

Khá dễ dàng để kết nối R với các hệ thống này bằng đầu nối cơ sở dữ liệu dựa trên DBI (có sẵn trên CRAN).


Touche vì sử dụng các công cụ cơ sở dữ liệu tốt hơn, nhưng vì điều đó sẽ liên quan đến rắc rối quản trị (chắc chắn phải yêu thích các quy định quản trị đó ở các công ty lớn), nên tôi đang cố gắng gắn bó với những gì mình có. Thêm vào đó, tôi đang nhắm đến việc chuyển đổi giữa các tệp văn bản mà tôi nhận được càng ít càng tốt.
FTWynn

3

scan () có cả đối số nlines và đối số bỏ qua. Có lý do nào đó mà bạn có thể sử dụng nó để đọc từng dòng một, kiểm tra ngày tháng xem có phù hợp không? Nếu tệp đầu vào được sắp xếp theo ngày, bạn có thể lưu trữ một chỉ mục cho bạn biết những gì bạn bỏ qua và nlines sẽ tăng tốc quá trình trong tương lai.


Tôi sẽ kiểm tra nó, nhưng tệp không được sắp xếp theo bất kỳ thứ gì hữu ích như ngày tháng. Các nhà cung cấp dường như nghĩ rằng điều quan trọng hơn để sắp xếp theo những gì khu vực một quận cho là trong / tiếng thở dài ....
FTWynn

Tôi nghĩ rằng bạn đã hiểu sai đề xuất của anh ấy: đọc tệp của bạn theo từng đoạn, và chỉ trích xuất các hàng bạn cần từ mỗi đoạn. Các tệp không cần phải được đặt hàng.
Karl Forner

1

Ngày nay, 3,5GB thực sự không quá lớn, tôi có thể truy cập vào một máy có RAM 244GB (r3,8xlarge) trên đám mây Amazon với giá 2,80 USD / giờ. Bạn sẽ mất bao nhiêu giờ để tìm ra cách giải quyết vấn đề bằng cách sử dụng các giải pháp kiểu dữ liệu lớn? Bao nhiêu là thời gian của bạn có giá trị? Có, bạn sẽ mất một hoặc hai giờ để tìm ra cách sử dụng AWS - nhưng bạn có thể tìm hiểu những kiến ​​thức cơ bản trên một cấp miễn phí, tải dữ liệu lên và đọc 10k dòng đầu tiên vào R để kiểm tra xem nó hoạt động và sau đó bạn có thể kích hoạt ví dụ bộ nhớ lớn như r3.8xlarge và đọc tất cả trong! Chỉ là 2c của tôi.


0

Bây giờ, năm 2017, tôi khuyên bạn nên sử dụng spark và sparkR.

  • cú pháp có thể được viết theo cách đơn giản tương tự như dplyr

  • nó khá phù hợp với bộ nhớ nhỏ (nhỏ theo nghĩa của năm 2017)

Tuy nhiên, nó có thể là một trải nghiệm đáng sợ khi bắt đầu ...


-3

Tôi sẽ tìm DB và sau đó thực hiện một số truy vấn để trích xuất các mẫu bạn cần thông qua DBI

Vui lòng tránh nhập tệp csv 3,5 GB vào SQLite. Hoặc ít nhất hãy kiểm tra kỹ xem db HUGE của bạn có phù hợp với các giới hạn của SQLite không, http://www.sqlite.org/limits.html

Đó là một DB lớn chết tiệt mà bạn có. Tôi sẽ sử dụng MySQL nếu bạn cần tốc độ. Nhưng hãy chuẩn bị để đợi rất nhiều giờ để quá trình nhập kết thúc. Trừ khi bạn có một số phần cứng độc đáo hoặc bạn đang viết từ tương lai ...

EC2 của Amazon cũng có thể là một giải pháp tốt để khởi tạo máy chủ chạy R và MySQL.

giá trị hai xu khiêm tốn của tôi.


18
3.5Gb lớn như thế nào cho sqlite? Chừng nào bạn đang sử dụng một hệ thống tập tin thích hợp không nên có vấn đề (tôi thường xuyên sử dụng> 30GB sqlite dbs cho các ứng dụng người dùng duy nhất)
Aaron Statham
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.