Làm sạch dữ liệu định dạng không nhất quán trong R?


16

Tôi thường xử lý dữ liệu khảo sát lộn xộn đòi hỏi phải dọn dẹp nhiều trước khi có thể thực hiện bất kỳ số liệu thống kê nào. Tôi đã từng làm điều này "thủ công" trong Excel, đôi khi sử dụng các công thức Excel và đôi khi kiểm tra từng mục một. Tôi bắt đầu thực hiện ngày càng nhiều các nhiệm vụ này bằng cách viết các tập lệnh để thực hiện chúng trong R, điều này rất có lợi (lợi ích bao gồm ghi lại những gì đã làm, ít có lỗi và có thể sử dụng lại mã nếu tập dữ liệu là cập nhật).

Nhưng vẫn còn một số loại dữ liệu mà tôi gặp khó khăn khi xử lý hiệu quả. Ví dụ:

> d <- data.frame(subject = c(1,2,3,4,5,6,7,8,9,10,11),
+   hours.per.day = c("1", "2 hours", "2 hr", "2hr", "3 hrs", "1-2", "15 min", "30 mins", "a few hours", "1 hr 30 min", "1 hr/week"))
> d
   subject hours.per.day
1        1             1
2        2       2 hours
3        3          2 hr
4        4           2hr
5        5         3 hrs
6        6           1-2
7        7        15 min
8        8       30 mins
9        9   a few hours
10      10   1 hr 30 min
11      11     1 hr/week

hours.per.daycó nghĩa là số giờ trung bình mỗi ngày dành cho một hoạt động nhất định, nhưng những gì chúng ta có chính xác là những gì chủ đề đã viết. Giả sử tôi đưa ra một số quyết định về việc phải làm gì với các phản hồi mơ hồ và tôi muốn biến được dọn dẹp hours.per.day2như sau.

   subject hours.per.day hours.per.day2
1        1             1      1.0000000
2        2       2 hours      2.0000000
3        3          2 hr      2.0000000
4        4           2hr      2.0000000
5        5         3 hrs      3.0000000
6        6           1-2      1.5000000
7        7        15 min      0.2500000
8        8       30 mins      0.5000000
9        9   a few hours      3.0000000
10      10   1 hr 30 min      1.5000000
11      11     1 hr/week      0.1428571

Giả sử rằng số lượng các trường hợp là khá lớn (giả sử 1000) và biết rằng các đối tượng được tự do viết bất cứ điều gì họ thích, cách tốt nhất để tiếp cận điều này là gì?

Câu trả lời:


12

Tôi sẽ sử dụng gsub () để xác định các chuỗi mà tôi biết và sau đó có thể làm phần còn lại bằng tay.

test <- c("15min", "15 min", "Maybe a few hours", 
          "4hr", "4hour", "3.5hr", "3-10", "3-10")
new_var <- rep(NA, length(test))

my_sub <- function(regex, new_var, test){
    t2 <- gsub(regex, "\\1", test)
    identified_vars <- which(test != t2)
    new_var[identified_vars] <- as.double(t2[identified_vars])
    return(new_var)    
}

new_var <- my_sub("([0-9]+)[ ]*min", new_var, test)
new_var <- my_sub("([0-9]+)[ ]*(hour|hr)[s]{0,1}", new_var, test)

Để có được công việc với những người bạn cần thay đổi bằng tay, tôi đề xuất một cái gì đó như thế này:

# Which have we not found
by.hand <- which(is.na(new_var))

# View the unique ones not found
unique(test[by.hand])
# Create a list with the ones
my_interpretation <- list("3-10"= 5, "Maybe a few hours"=3)
for(key_string in names(my_interpretation)){
    new_var[test == key_string] <- unlist(my_interpretation[key_string])
}

Điều này mang lại:

> new_var
[1] 15.0 15.0  3.0  4.0  4.0  3.5  5.0  5.0

Regex có thể hơi rắc rối, mỗi khi tôi làm bất cứ điều gì với regex tôi đều chạy một vài bài kiểm tra đơn giản. Se? Regex cho hướng dẫn. Đây là một số hành vi cơ bản:

> # Test some regex
> grep("[0-9]", "12")
[1] 1
> grep("[0-9]", "12a")
[1] 1
> grep("[0-9]$", "12a")
integer(0)
> grep("^[0-9]$", "12a")
integer(0)
> grep("^[0-9][0-9]", "12a")
[1] 1
> grep("^[0-9]{1,2}", "12a")
[1] 1
> grep("^[0-9]*", "a")
[1] 1
> grep("^[0-9]+", "a")
integer(0)
> grep("^[0-9]+", "12222a")
[1] 1
> grep("^(yes|no)$", "yes")
[1] 1
> grep("^(yes|no)$", "no")
[1] 1
> grep("^(yes|no)$", "(yes|no)")
integer(0)
> # Test some gsub, the \\1 matches default or the found text within the ()
> gsub("^(yes|maybe) and no$", "\\1", "yes and no")
[1] "yes"

Cảm ơn câu trả lời Max. Tôi không quen thuộc với các biểu thức thông thường nên sẽ phải tìm hiểu về chúng. Bạn có phiền khi đưa ra một mô tả ngắn gọn về cách bạn sẽ làm phần còn lại bằng tay không? Có cách nào tốt hơn là chỉ làm một cái gì đó giống như new_var[by.hand] <- c(2, 1, ...)với by.handviệc TRUEcho các trường hợp được thực hiện bằng tay?
đánh dấu999

@ mark999: Đã thêm một số ví dụ và gợi ý về cách bạn có thể làm những cái đó bằng tay.
Tối đa

1
Các biểu thức thông thường cực kỳ quan trọng đối với bất kỳ loại thao tác dữ liệu nào: làm sạch dữ liệu như OP có hoặc để trích xuất dữ liệu từ các tệp, HTML, v.v. (Đối với HTML thích hợp, có các thư viện, muốn XMLgiúp bạn trích xuất dữ liệu, nhưng điều này không hoạt động khi HTML không đúng định dạng.)
Wayne

6

@ Đề nghị của Max là một trong những tốt. Có vẻ như nếu bạn viết một thuật toán nhận ra các con số cũng như các từ / chữ viết tắt liên quan đến thời gian phổ biến, bạn sẽ nhận được phần lớn cách đó. Đây sẽ không phải là mã đẹp, nhưng nó sẽ hoạt động và bạn có thể cải thiện nó theo thời gian khi bạn gặp các trường hợp vấn đề.

Nhưng để có cách tiếp cận mạnh mẽ hơn (và ban đầu tốn thời gian), hãy thử Googling "phân tích chuỗi thời gian ngôn ngữ tự nhiên". Một số phát hiện thú vị là API thời gian mở này , một mô-đun Python tốt và một trong nhiều luồng xử lý giống như thế này trên Stack Overflow .

Về cơ bản, phân tích ngôn ngữ tự nhiên là một vấn đề phổ biến và bạn nên tìm giải pháp bằng các ngôn ngữ khác ngoài R. Bạn có thể xây dựng các công cụ bằng ngôn ngữ khác mà bạn có thể truy cập bằng R, hoặc ít nhất bạn có thể có được ý tưởng tốt cho thuật toán của riêng mình.


4

Đối với một cái gì đó tương tự, nếu nó đủ dài, tôi nghĩ rằng tôi muốn có một danh sách các biểu thức chính quy và quy tắc chuyển đổi và đưa các giá trị mới sang một cột khác (để bạn luôn có cơ hội kiểm tra lại mà không cần tải lại dữ liệu thô) ; RE sẽ được áp dụng để dữ liệu không được chuyển đổi quá xa cho đến khi tất cả dữ liệu được chuyển đổi hoặc tất cả các quy tắc đã hết. Có lẽ tốt nhất là giữ một danh sách các giá trị logic cho biết hàng nào chưa được chuyển đổi.

Một vài quy tắc như vậy là hiển nhiên và có thể sẽ xử lý 80-90% các trường hợp, nhưng vấn đề là sẽ luôn có một số bạn không biết sẽ xuất hiện (mọi người rất sáng tạo).

Sau đó, bạn cần một tập lệnh đi qua và trình bày bản gốc của các giá trị quy tắc chưa được chuyển đổi theo danh sách các quy tắc rõ ràng cho bạn một lần, cho bạn cơ hội để thực hiện biểu thức chính quy (nói ) để xác định các trường hợp đó và đưa ra một biến đổi mới cho các trường hợp phù hợp với nó, nó bổ sung vào danh sách ban đầu và áp dụng cho các hàng chưa được chuyển đổi của vectơ gốc trước khi kiểm tra xem có trường hợp nào còn lại để trình bày cho bạn không .

Cũng có thể hợp lý khi có tùy chọn bỏ qua một trường hợp (để bạn có thể tiếp tục với những trường hợp dễ dàng hơn), vì vậy bạn có thể giải quyết các trường hợp rất khó khăn cho đến cuối.

Trường hợp xấu nhất, bạn làm một vài bằng tay.

Sau đó, bạn có thể giữ danh sách đầy đủ các quy tắc bạn tạo, để áp dụng lại khi dữ liệu phát triển hoặc một bộ dữ liệu tương tự mới xuất hiện.

Tôi không biết nếu nó tiếp cận thực tiễn tốt nhất từ ​​xa (tôi nghĩ sẽ cần một thứ gì đó trang trọng hơn nhiều), nhưng về mặt xử lý một lượng lớn dữ liệu như vậy một cách nhanh chóng, nó có thể có giá trị.


Cảm ơn câu trả lời, Glen. Nghe có vẻ rất hấp dẫn. Bạn có thấy đó là một lợi thế lớn khi các giá trị chưa được chuyển đổi được trình bày cùng một lúc, trái ngược với việc chỉ hiển thị tất cả chúng và nhìn vào đầu ra đó không? Tôi chưa bao giờ làm bất cứ điều gì như có những thứ được trình bày cùng một lúc.
đánh dấu999

1
@ mark999, tôi sẽ nghĩ rằng có cả ưu điểm và nhược điểm của việc trình bày một lần. Ưu điểm là sự đơn giản - sử dụng cat () để hiển thị thời gian không rõ ràng và quét () để ghi lại diễn giải của bạn về thời gian đó rất dễ thực hiện. Nhược điểm là bạn có thể bỏ lỡ bức tranh lớn về rất nhiều mục mà bạn có thể sửa lỗi với một dòng mã regex duy nhất. Bạn có thể có suy nghĩ về những gì bạn hy vọng nhận được: nếu bạn chỉ muốn giải quyết vấn đề này, hãy làm nó bằng tay. Nếu bạn muốn tìm hiểu thêm về R, hãy thử viết mã giải pháp.
Tro

Xin lỗi vì đã không trả lời; Tôi đồng ý rộng rãi với nhận xét của Ash
Glen_b -Reinstate Monica

4

R có chứa một số tiêu chuẩn chức năng cho thao tác dữ liệu, có thể được sử dụng để làm sạch dữ liệu, trong mình căn cứ gói ( gsub, transform, vv), cũng như trong các gói của bên thứ ba khác nhau, chẳng hạn như stringr , Reshape , reshape2 , và plyr . Các ví dụ và thực tiễn sử dụng tốt nhất cho các gói này và các chức năng của chúng được mô tả trong bài viết sau: http://vita.had.co.nz/ con / tidy-data.pdf .

Ngoài ra, R cung cấp một số gói đặc biệt tập trung vào làm sạch và chuyển đổi dữ liệu:

Một cách tiếp cận toàn diện và chặt chẽ để dữ liệu làm sạch trong R, bao gồm các ví dụ và sử dụng editrulesdeducorrect gói, cũng như mô tả về công việc ( framework ) của dữ liệu làm sạch trong R, được trình bày trong các giấy tờ sau đây, mà tôi khuyên bạn nên: http : //cran.r-project.org/doc/contrib/de_Jrid+van_der_Loo-Int sinhtion_to_data_cleaning_with_R.pdf .

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.