2012-01-02 36 views
6

Tôi có dữ liệu văn bản (trong R) và muốn thay thế một số ký tự bằng các ký tự khác trong một khung dữ liệu. Tôi nghĩ rằng đây sẽ là một nhiệm vụ dễ dàng bằng cách sử dụng strsplit trên không gian và tạo ra một véc tơ mà tôi có thể sau đó sử dụng kết hợp (% in%) mà sau đó có thể được dán lại với nhau. Nhưng rồi tôi nghĩ về dấu chấm câu. Không có dấu cách giữa từ cuối cùng của câu và dấu chấm câu ở cuối.Thay thế gsub có điều kiện

Tôi có thể có một cách đơn giản hơn để đạt được những gì tôi muốn hơn là mớ hỗn độn phức tạp đang trở thành mã của tôi. Tôi sẽ đánh giá cao hướng với vấn đề này.

#Character String 
x <- "I like 346 ice cream cones. They're 99 percent good! I ate 46." 

#Replacement Values Dataframe 
    symbol text      
1 "346" "three hundred forty six" 
2 "99" "ninety nine"    
3 "46" "forty six" 

#replacement dataframe 
numDF <- 
data.frame(symbol = c("346","99", "46"), 
      text = c("three hundred forty six", "ninety nine","forty six"), 
      stringsAsFactors = FALSE) 

kết quả mong muốn:

[1] "I like three hundred forty six ice cream cones. They're ninety nine percent good! You ate forty six?") 

EDIT: Tôi ban đầu được hưởng gsub có điều kiện này bởi vì đó là những gì nó có vẻ như với tôi mặc dù không có gsub liên quan.

+1

Dữ liệu 'dput''ed của bạn không được đánh giá vào data.frame. Tôi vừa chỉnh sửa bài đăng của bạn để nó hiện tại. Hy vọng bạn không nhớ :) –

+0

Xin lỗi về Josh đó. Cảm ơn bạn đã quan tâm đến điều đó và cho câu trả lời của bạn. Tôi không biết về gói gsubfn. Cảm ơn vì chỉ ra điều ấy. –

Trả lời

8

Có lẽ đây, lấy cảm hứng từ câu trả lời Josh O'Brien của, hiện nó:

x <- "I like 346 ice cream cones. They're 99 percent good! I ate 46." 
numDF <- structure(c("346", "99", "46", "three hundred forty six", "ninety nine", 
"forty six"), .Dim = c(3L, 2L), .Dimnames = list(c("1", "2", 
"3"), c("symbol", "text"))) 

pat <- paste(numDF[,"symbol"], collapse="|") 
repeat { 
    m <- regexpr(pat, x) 
    if(m==-1) break 
    sym <- regmatches(x,m) 
    regmatches(x,m) <- numDF[match(sym, numDF[,"symbol"]), "text"] 
} 
x 
+0

Đẹp Tất cả ba câu trả lời đều hoạt động nhưng máy của bạn là hướng thẳng nhất trong khi vẫn ở trong cơ sở. Cảm ơn bạn. –

+0

+1 - Rất hay khi thấy được sử dụng tốt cho 'regmatches'. –

6

giải pháp này sử dụng gsubfn trong gói cùng tên:

library(gsubfn) 

(pat <- paste(numDF$symbol, collapse="|")) 
# [1] "346|99|46" 

gsubfn(pattern = pat, 
     replacement = function(x) { 
      numDF$text[match(x, numDF$symbol)] 
     }, 
     x) 
[1] "I like three hundred forty six ice cream cones. They're ninety nine percent good! I ate forty six." 
+0

Josh Tôi thích nó và không xác định điều này nhưng điều này là cho một gói và tôi đang cố gắng không dựa vào bất cứ điều gì ngoại trừ chức năng cơ bản. +1 –

4

Bạn có thể chia vào khoảng trắng hoặc từ ranh giới (mà sẽ phù hợp giữa một từ và dấu chấm câu):

> x 
[1] "I like 346 ice cream cones. They're 99 percent good! I ate 46." 
> strsplit(x, split='\\s|\\>|\\<') 
[[1]] 
[1] "I"  "like" "346"  "ice"  "cream" "cones" "."  
[8] ""  "They" "'re"  "99"  "percent" "good" "!"  
[15] ""  "I"  "ate"  "46"  "."  

Sau đó, bạn có thể làm thay thế của bạn.

+0

Tôi đã làm cho nó hoạt động với câu trả lời của bạn nhưng câu trả lời của Karsten W. chỉ là một chút neater và nhanh hơn. Cảm ơn đã giúp đỡ. +1 –

+0

Tôi thích điều này, nhưng có vẻ như sẽ rất khó khăn để dán kết quả đã xử lý lại với nhau, với khoảng cách giữa một số chuỗi chứ không phải các chuỗi khác. Và nếu có * đôi khi * một dấu cách giữa một từ và câu chấm dứt dấu câu, bạn chắc chắn sẽ mất rằng: 'x <-" word. Word. "; strsplit (x, split = '\\ s | \\> | \\ <') [[1]] '. –

+0

@Josh O'Brien nó đã làm việc cho tôi bằng cách sử dụng gsub và tìm kiếm dấu chấm câu sau đây ('?.!) Và một không gian hàng đầu và đưa nó ra cho dấu chấm câu đó trừ đi không gian. Điều này đã thêm 4 dòng mã (tôi chắc chắn có một cách nhanh hơn) nhưng nó thực sự làm việc. –

2

Đó là không chính xác rõ ràng cho dù bạn thực sự muốn chuyển đổi chữ số tương đương alpha của họ. Nếu vậy thì đây là một chiến lược tổng quát hơn nhiều. Có (ít nhất) hai hàm số để chuyển đổi văn bản trong lưu trữ rhelp: digits2text của Jim Lemon và numberstowords của John Fox. Tôi cũng chuyển sang gregexpr để có được một cách tiếp cận vectorized:

cắt và dán Lemon's function from the HTML found here làm việc ra khỏi hộp:

>  m <- gregexpr("[0-9]+", x) 
>  sym <- regmatches(x,m) 
>  regmatches(x,m) <- digits2text(as.numeric(sym[[1]])) 
illion = 0 
digilen = 3 
digitext = three hundred forty six 
[1] 6 4 3 
> 
> x 
[1] "I like three hundred forty six ice cream cones. They're three hundred forty six percent good! I ate three hundred forty six." 

tôi cần phải làm một số chỉnh sửa của numberstowords vì đã có một số linefeeds thiếu đó messed up phân tích cú pháp (và tôi bao gồm phiên bản thành công dưới sự chứng minh này:

>  m <- gregexpr("[0-9]+", x) 
>  sym <- regmatches(x,m) 
>  regmatches(x,m) <- numbers2words(as.numeric(sym[[1]])) 
> 
> x 
[1] "I like three hundred forty six ice cream cones. They're three hundred forty six percent good! I ate three hundred forty six." 

chức năng của Fox thay đổi nội dung từ: http://tolstoy.newcastle.edu.au/R/help/05/04/2715.html

numbers2words <- function(x){ 

    helper <- function(x){ 

     digits <- rev(strsplit(as.character(x), "")[[1]]) 
     nDigits <- length(digits) 
     if (nDigits == 1) as.vector(ones[digits]) 
     else if (nDigits == 2) 
      if (x <= 19) as.vector(teens[digits[1]]) 
       else trim(paste(tens[digits[2]], 
          Recall(as.numeric(digits[1])))) 
     else if (nDigits == 3) trim(paste(ones[digits[3]], "hundred", 
      Recall(makeNumber(digits[2:1])))) 
     else { 
      nSuffix <- ((nDigits + 2) %/% 3) - 1 
      if (nSuffix > length(suffixes)) stop(paste(x, "is too large!")) 
      trim(paste(Recall(makeNumber(digits[ 
       nDigits:(3*nSuffix + 1)])), 
       suffixes[nSuffix], 
       Recall(makeNumber(digits[(3*nSuffix):1])))) 
      } 
     } 
    trim <- function(text){ 
     gsub("^\ ", "", gsub("\ *$", "", text)) 
     }  


    makeNumber <- function(...) as.numeric(paste(..., collapse="")) 
    opts <- options(scipen=100) 
    on.exit(options(opts)) 
    ones <- c("", "one", "two", "three", "four", "five", "six", "seven", 

     "eight", "nine") 
    names(ones) <- 0:9 
    teens <- c("ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", 

     "sixteen", " seventeen", "eighteen", "nineteen") 
    names(teens) <- 0:9 
    tens <- c("twenty", "thirty", "forty", "fifty", "sixty", 
       "seventy", "eighty", "ninety") 
    names(tens) <- 2:9 
    x <- round(x) 
    suffixes <- c("thousand", "million", "billion", "trillion") 
    if (length(x) > 1) return(sapply(x, helper)) 
    helper(x) 
    } 
+0

DWin bạn đúng trong đó tôi muốn lấy văn bản và chỉ ra số cho văn bản. Ban đầu tôi đăng câu hỏi này trên talkstats.com http://www.talkstats.com/showthread.php/22564-Replacement-in-gsub-as-a-function-argument và tìm thấy hàm Fox. Tôi đã nhận được một số trợ giúp từ bryangoodrich có nhưng đã ở một bế tắc trong quá trình này trong việc đưa số vào văn bản thay thế trở lại vào văn bản gốc. Câu hỏi này đặc biệt hơn trong việc đối phó với mảnh ghép đó. + 1 –

+0

Thêm câu hỏi của tôi về việc thay thế các giá trị số bằng các từ tương đương mà tôi đã đăng trên talkstats thì cụ thể hơn đối với tôi. Câu hỏi về gsubbing có điều kiện được tổng quát hơn rất nhiều đối với nhiều người không chỉ là những người xử lý các giá trị số. Tôi có thể sử dụng một cách tiếp cận tương tự trong một chức năng thay thế viết tắt tôi cần phải biên dịch. –

+0

Tôi nhận thấy rằng các thay thế của tôi không đi qua các giá trị số đúng cách. –

3

Một giải pháp khác sử dụng Reduce từ base.

list_df <- apply(numDF, 1, as.list) 
Reduce(function(x, l) gsub(l$symbol, l$text, x), list_df, init = x) 

EDIT. Dưới đây là giải pháp hoàn chỉnh sử dụng chức năng numbers2words trực tiếp ..

list_df <- as.numeric(regmatches(x, gregexpr('[0-9]+', x))[[1]]) 
Reduce(function(x, l) gsub(l, numbers2words(l), x), list_df, init = x) 
Các vấn đề liên quan