2016-04-15 23 views
7

Đây là câu hỏi đầu tiên của tôi về SO vì vậy hãy cho tôi biết nếu nó có thể được cải thiện. Tôi đang làm việc trên một dự án xử lý ngôn ngữ tự nhiên trong R và đang cố gắng để xây dựng một data.table có chứa các trường hợp thử nghiệm. Ở đây, tôi xây dựng một nhiều ví dụ đơn giản:Cột chia tách dữ liệu có thể tạo ra NAs

texts.dt <- data.table(string = c("one", 
            "two words", 
            "three words here", 
            "four useless words here", 
            "five useless meaningless words here", 
            "six useless meaningless words here just", 
            "seven useless meaningless words here just to", 
            "eigth useless meaningless words here just to fill", 
            "nine useless meaningless words here just to fill up", 
            "ten useless meaningless words here just to fill up space"), 
         word.count = 1:10, 
         stop.at.word = c(0, 1, 2, 2, 4, 3, 3, 6, 7, 5)) 

này trả về data.table chúng tôi sẽ làm việc trên:

              string word.count stop.at.word 
1:              one   1   0 
2:            two words   2   1 
3:           three words here   3   2 
4:         four useless words here   4   2 
5:      five useless meaningless words here   5   4 
6:     six useless meaningless words here just   6   3 
7:    seven useless meaningless words here just to   7   3 
8:  eigth useless meaningless words here just to fill   8   6 
9:  nine useless meaningless words here just to fill up   9   7 
10: ten useless meaningless words here just to fill up space   10   5 

Trong ứng dụng thực tế, giá trị trong cột stop.at.word được xác định một cách ngẫu nhiên (với giới hạn trên = word.count - 1). Ngoài ra, các chuỗi không được sắp xếp theo chiều dài nhưng điều đó không tạo nên sự khác biệt.

Mã nên thêm hai cột inputoutput, nơi input chứa chuỗi con từ vị trí 1 đến stop.at.wordoutput chứa các từ mà sau (từ đơn), như vậy:

>desired_result 
                  string word.count stop.at.word          input 
    1:              one   1   0            
    2:            two words   2   1           two 
    3:           three words here   3   2         three words 
    4:         four useless words here   4   2        four useless 
    5:      five useless meaningless words here   5   4    five useless meaningless words 
    6:     six useless meaningless words here just   6   2         six useless 
    7:    seven useless meaningless words here just to   7   3     seven useless meaningless 
    8:  eigth useless meaningless words here just to fill   8   6 eigth useless meaningless words here just 
    9:  nine useless meaningless words here just to fill up   9   7 nine useless meaningless words here just to 
    10: ten useless meaningless words here just to fill up space   10   5   ten useless meaningless words here 
      output 
    1:    
    2:  words 
    3:  here 
    4:  words 
    5:  here 
    6: meaningless 
    7:  words 
    8:   to 
    9:  fill 
    10:  just 

Đáng tiếc là những gì tôi có được thay vì là thế này:

             string word.count stop.at.word input output 
1:              one   1   0    
2:            two words   2   1 NA  NA 
3:           three words here   3   2 NA  NA 
4:         four useless words here   4   2 NA  NA 
5:      five useless meaningless words here   5   4 NA  NA 
6:     six useless meaningless words here just   6   3 NA  NA 
7:    seven useless meaningless words here just to   7   3 NA  NA 
8:  eigth useless meaningless words here just to fill   8   6 NA  NA 
9:  nine useless meaningless words here just to fill up   9   7 NA  NA 
10: ten useless meaningless words here just to fill up space   10   5 ten  NA 

Thông báo kết quả không phù hợp, với một chuỗi rỗng trên hàng 1 và "mười" trở lại trên hàng 10

Đây là mã tôi đang sử dụng:

texts.dt[, c("input", "output") := .(
     substr(string, 
       1, 
       sapply(gregexpr(" ", string),"[", stop.at.word) - 1), 
     substr(string, 
       sapply(gregexpr(" ", string),"[", stop.at.word), 
       sapply(gregexpr(" ", string),"[", stop.at.word + 1) - 1) 
    )] 

Tôi chạy nhiều xét nghiệm và substr hướng dẫn hoạt động tốt khi tôi cố gắng xâu cá nhân trong giao diện điều khiển, nhưng thất bại khi áp dụng cho các data.table. Tôi nghi ngờ tôi thiếu một cái gì đó liên quan đến phạm vi trong data.table, nhưng tôi đã không được sử dụng gói này cho lâu vì vậy tôi khá bối rối.

Tôi sẽ đánh giá cao một số trợ giúp. Cảm ơn bạn trước!

+3

Khiếu nại nhỏ: Cố gắng làm cho các ví dụ của bạn đủ nhỏ để chúng không yêu cầu cuộn ngang trong trình duyệt. – Frank

+1

@Franck - Chắc chắn, tôi sẽ làm tốt hơn lần sau! –

+0

Tôi không chắc tại sao hai câu trả lời khác lại bị xóa ..? @ProcrastinatusMaximus – eddi

Trả lời

5

tôi có lẽ sẽ làm

texts.dt[stop.at.word > 0, c("input","output") := { 
    sp = strsplit(string, " ") 
    list( 
    mapply(function(p,n) paste(p[seq_len(n)], collapse = " "), sp, stop.at.word), 
    mapply(`[`, sp, stop.at.word+1L) 
) 
}] 

# partial result 
head(texts.dt, 4) 

        string word.count stop.at.word  input output 
1:      one   1   0   NA  NA 
2:    two words   2   1   two words 
3:  three words here   3   2 three words here 
4: four useless words here   4   2 four useless words 

Cách khác:

library(stringi) 
texts.dt[stop.at.word > 0, c("input","output") := { 
    patt = paste0("((\\w+){", stop.at.word-1, "}\\w+) (.*)") 
    m = stri_match(string, regex = patt) 
    list(m[, 2], m[, 4]) 
}] 
5

Một thay thế cho @ giải pháp của Frank mapply đang sử dụng by = 1:nrow(texts.dt) với strsplitpaste:

library(data.table) 
texts.dt[, `:=` (input = paste(strsplit(string, ' ')[[1]][1:stop.at.word][stop.at.word>0], 
           collapse = " "), 
       output = strsplit(string, ' ')[[1]][stop.at.word + 1]), 
     by = 1:nrow(texts.dt)] 

mang đến cho:

> texts.dt 
                 string word.count stop.at.word          input output 
1:              one   1   0            one 
2:            two words   2   1           two words 
3:           three words here   3   2         three words here 
4:         four useless words here   4   2        four useless words 
5:      five useless meaningless words here   5   4    five useless meaningless words here 
6:     six useless meaningless words here just   6   3      six useless meaningless words 
7:    seven useless meaningless words here just to   7   3     seven useless meaningless words 
8:  eigth useless meaningless words here just to fill   8   6 eigth useless meaningless words here just  to 
9:  nine useless meaningless words here just to fill up   9   7 nine useless meaningless words here just to fill 
10: ten useless meaningless words here just to fill up space   10   5   ten useless meaningless words here just 

Thay vì sử dụng [[1]] bạn cũng có thể quấn strsplit trong unlist như sau: unlist(strsplit(string, ' ')) (thay vì strsplit(string, ' ')[[1]]). Điều này sẽ cho bạn kết quả tương tự.


Hai lựa chọn khác:

1) với stringi gói:

library(stringi) 
texts.dt[, `:=`(input = paste(stri_extract_all_words(string[stop.at.word>0], 
                simplify = TRUE)[1:stop.at.word], 
           collapse = " "), 
       output = stri_extract_all_words(string[stop.at.word>0], 
               simplify = TRUE)[stop.at.word+1]), 
     1:nrow(texts.dt)] 

2) hoặc một sự thích nghi từ this answer:

texts.dt[stop.at.word>0, 
     c('input','output') := tstrsplit(string, 
              split = paste0("(?=(?>\\s+\\S*){", 
                 word.count - stop.at.word, 
                 "}$)\\s"), 
              perl = TRUE) 
     ][, output := sub('(\\w+).*','\\1',output)] 

mà cả hai cung cấp cho:

> texts.dt 
                 string word.count stop.at.word          input output 
1:              one   1   0           NA  NA 
2:            two words   2   1           two words 
3:           three words here   3   2         three words here 
4:         four useless words here   4   2        four useless words 
5:      five useless meaningless words here   5   4    five useless meaningless words here 
6:     six useless meaningless words here just   6   3      six useless meaningless words 
7:    seven useless meaningless words here just to   7   3     seven useless meaningless words 
8:  eigth useless meaningless words here just to fill   8   6 eigth useless meaningless words here just  to 
9:  nine useless meaningless words here just to fill up   9   7 nine useless meaningless words here just to fill 
10: ten useless meaningless words here just to fill up space   10   5   ten useless meaningless words here just 
+1

Việc thích ứng chính xác sẽ sử dụng 'word.count - stop.at.word' hoặc tương tự thay vì' stop.at.word' – Frank

+1

@eddi được cập nhật với đề xuất của Frank – Jaap

5
dt[, `:=`(input = sub(paste0('((\\s*\\w+){', stop.at.word, '}).*'), '\\1', string), 
      output = sub(paste0('(\\s*\\w+){', stop.at.word, '}\\s*(\\w+).*'), '\\2', string)) 
    , by = stop.at.word][] 
#              string word.count stop.at.word 
# 1:              one   1   0 
# 2:            two words   2   1 
# 3:           three words here   3   2 
# 4:         four useless words here   4   2 
# 5:      five useless meaningless words here   5   4 
# 6:     six useless meaningless words here just   6   3 
# 7:    seven useless meaningless words here just to   7   3 
# 8:  eigth useless meaningless words here just to fill   8   6 
# 9:  nine useless meaningless words here just to fill up   9   7 
#10: ten useless meaningless words here just to fill up space   10   5 
#           input output 
# 1:            one 
# 2:           two words 
# 3:         three words here 
# 4:        four useless words 
# 5:    five useless meaningless words here 
# 6:      six useless meaningless words 
# 7:     seven useless meaningless words 
# 8: eigth useless meaningless words here just  to 
# 9: nine useless meaningless words here just to fill 
#10:   ten useless meaningless words here just 

Tôi không chắc tôi hiểu được logic cho output là gì cho dòng đầu tiên, nhưng những sửa chữa nhỏ, nếu thực sự cần thiết, còn lại để OP.

+0

@eddi Cảm ơn và bạn đúng, không có logic; Tôi nên để lại "một" trong cột đầu ra. Nhưng khi tôi áp dụng nó vào data.frame lớn của tôi, giải pháp của bạn trả về một lỗi: ' ' Lỗi trong phụ (paste0 ("((\\ s * \\ w +) {", stop.at.word, "}) . * ")," \\ 1 ", chuỗi): cụm từ thông dụng không hợp lệ '((\ s * \ w +) {308}). *', Lý do 'Nội dung không hợp lệ của {}' Bất kỳ ý tưởng nào? –

+1

@Luc Rõ ràng có những giới hạn về định lượng regex hữu hạn. Có thể bạn đang truy cập http://www.perlmonks.org/?node_id=649090 – Frank

+1

Rõ ràng, giới hạn là '255'. Hãy thử 'x = dán (rep (" A ", 400), collapse =" "); grep ("A {256}", x) ' – Frank

Các vấn đề liên quan