2016-10-21 7 views
0

私が作成した電話機検証関数に入力として使用するいくつかのカラムを持つデータテーブルがあります。カラム入力のある関数からdata.tableカラムを割り当て

library(data.table) 
dt <- data.table(ID = c(1:6), 
       phone = c("0412 345 789","0438 123 456", 
          "041 2345 543", "(02) 1234 5678", 
          "9876 1234", "04123456789"), 
       state = c("NSW","QLD","SA"), 
       country = c("AU"), 
       phone_countries = c("AU","AU","AU","AU,US","AU,US","AU,US")) 

# ID   phone state country phone_countries 
# 1: 1 0412 345 789 NSW  AU    AU 
# 2: 2 0438 123 456 QLD  AU    AU 
# 3: 3 041 2345 543 SA  AU    AU 
# 4: 4 (02) 1234 5678 NSW  AU   AU,US 
# 5: 5  9876 1234 QLD  AU   AU,US 
# 6: 6 04123456789 SA  AU   AU,US 

機能isValidPhoneは、このようになります(いくつかの異なる場所で電話番号を検証するために設計されています。私は簡潔にするために正規表現の一部を省略しています。)

isValidPhone <- function(phone, state, country, validation_countries) { 

    if (!(country %in% unlist(strsplit(validation_countries, ",")))) 
    return(FALSE) 

    # remove whitespace, hyphens and brackets 
    phone_clean <- gsub("[[:space:]]|-|\\.|\\(|\\)", "", phone) 

    if (is.na(phone_clean) | phone_clean == '' | is.na(iconv(phone_clean, "", "ASCII"))) 
    return(FALSE) 

    if (country == "AU") { 
    # append state area code if length is 8 digits 
    #print(paste("phone:", phone_clean, "state:", state)) 
    if (nchar(phone_clean, "width") == 8) 
     if (state %in% c('ACT', 'NSW', 'QLD', 'VIC', 'TAS', 'SA', 'NT', 'WA')) 
     phone_clean <- switch (state, 
     'ACT' = paste0("02",phone_clean), 
     'NSW' = paste0("02",phone_clean), 
     'QLD' = paste0("07",phone_clean), 
     'VIC' = paste0("03",phone_clean), 
     'TAS' = paste0("03",phone_clean), 
     'SA' = paste0("08",phone_clean), 
     'NT' = paste0("08",phone_clean), 
     'WA' = paste0("08",phone_clean)) 

    if (nchar(phone_clean, "width") == 9) 
     if(substr(phone_clean,1,1) %in% c(2:4,7,8)) 
     phone_clean <- paste0("0", phone_clean) 

    return(grepl("^(?:\\+?61|0)[23478](?:[ -]?[0-9]){8}$", 
       as.character(phone_clean), ignore.case=TRUE)) 
    } 
} 

私は、フィールドを割り当てています私data.tabledtに残念ながら、私が使用したのですvalidphone

dt[, validphone := isValidPhone(phone, state, country, phone_countries), by = 1:nrow(dt)] 

# ID   phone state country phone_countries validphone 
# 1: 1 0412 345 789 NSW  AU    AU  TRUE 
# 2: 2 0438 123 456 QLD  AU    AU  TRUE 
# 3: 3 041 2345 543 SA  AU    AU  TRUE 
# 4: 4 (02) 1234 5678 NSW  AU   AU,US  TRUE 
# 5: 5  9876 1234 QLD  AU   AU,US  TRUE 
# 6: 6 04123456789 SA  AU   AU,US  FALSE 

と呼ばby = 1:nrow(dt)は、私がやっていないかのように、現在の見せかけのように、完全な列のデータを問題の原因となるパラメータに渡します。これは私の実際のデータセット(〜300K)と多くの関数呼び出しにつながり、パフォーマンスは低下します。

私はベクトル化された関数を使うほうがよいと読んだが、どうやってこれを行うことができるかはわかりません。

希望の結果を達成するためのより効率的な方法はありますか?

+0

2番目の国の列の値は1つだけです。 –

+0

その列はコンマ区切りの値のリストです。 'phone_countries'は時々" AU、US、UK "のように見えます。私は機能の一部が私の質問の対象に影響を与えるとは思わない。 – Dan

+0

'nchar(...、" width ")'はあなたが望むものです – HubertL

答えて

0

ベクトルにあなたの機能を使用できるようにするために必要ないくつかのリエンジニアリングがあります:

主にフィルター行にFALSEを割り当てることによってif(...) return(FALSE)を交換し、最初return =>最後に逆の順番(最後の単語でそれらを評価します単語を最後に割り当てます)

switchifelseに置き換える必要があります。

あなたはこのようなもので終わる:あなたは `unlist`と`これはちょうど、Aに1行ずつ実行された場合、「検証国」で使用されているin`を持っている理由私は理解していない

isValidPhone <- function(phone, state, country, validation_countries) { 
    phone_clean <- gsub("[[:space:]]|-|\\.|\\(|\\)", "", phone) 

    AddArea <- country == "AU" & nchar(phone_clean) == 8 & 
    state %in% c('ACT', 'NSW', 'QLD', 'VIC', 'TAS', 'SA', 'NT', 'WA') 
    phone_clean[AddArea] <- ifelse(state[AddArea]%in%c('ACT','NSW'), 
           paste0("02",phone_clean[AddArea]), 
           ifelse(state[AddArea]%in%c('VIC','TAS'), 
             paste0("03",phone_clean[AddArea]), 
             ifelse(state[AddArea]%in%c('SA','NT', 'WA'), 
               paste0("08",phone_clean[AddArea]), 
               paste0("02",phone_clean[AddArea])))) 

    AddZero <- nchar(phone_clean) == 9 & substr(phone_clean,1,1) %in% c(2:4,7,8) 
    phone_clean[AddZero] <- paste0("0", phone_clean[AddZero]) 

    ans <- grepl("^(?:\\+?61|0)[23478](?:[ -]?[0-9]){8}$", 
         as.character(phone_clean), ignore.case=TRUE) 

    ans[(!(country %in% unlist(strsplit(validation_countries, ",")))) | 
     is.na(phone_clean) | phone_clean == '' | 
     is.na(iconv(phone_clean, "", "ASCII"))] <- FALSE 
    return(ans) 
} 
+0

これを使って亀裂がありますが、私はdata.tableで遭遇していた厄介なデータを扱うために少し変更する必要があるかもしれないと思います – Dan

+0

最初の 'phone_clean <-gsub ...'の後に 'phone_clean < - iconv(phone_clean、"、 "ASCII") 'を挿入するコードを少し修正しました。これは、ASCIIではないすべてのデータを「NA」に設定し、電話欄にあったダディーなマルチバイト文字を扱うことを意味します。関数の最後の式から 'iconv()'コードを削除することができました。また、機能の仕組みを調整することで、34秒から2秒以下になりました。 – Dan

関連する問題