2016-10-21 61 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

我不明白你爲什麼'unlist'和'in'的「驗證的國家」正在使用,如果這僅僅是在執行一行時間和第二國家專欄只有一個價值? –

+0

該列是逗號分隔值的列表。 'phone_countries'有時看起來像「AU,US,UK」。我不相信功能的一部分會影響我的問題的對象。 – Dan

+0

不知道'nchar(...,「width」)'是你想要的 – HubertL

回答

0

還有就是要能夠使用功能上的矢量需要一些重新設計:

主要通過過濾的行分配FALSE更換if(...) return(FALSE),並以相反的順序對其進行評估(硬道理第一return =>末頁字到末尾任務)

switch也需要替換爲ifelse

你最終的東西是這樣的:

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()'代碼。同樣通過調整函數的工作方式,它從34s到不到2s。 – Dan