2015-06-17 43 views
4

假設我有一個縣名單,其拼寫錯誤數量不同或其他問題與2010 FIPS dataset(用於創建fips數據框的代碼如下)不同,但拼寫錯誤的縣所在的州正確輸入。這裏有一個sample 21的隨機觀察從我的完整數據集:R:使用plyr在兩個數據源的匹配子集之間執行模糊字符串匹配

tomatch <- structure(list(county = c("Beauregard", "De Soto", "Dekalb", "Webster", 
            "Saint Joseph", "West Feliciana", "Ketchikan Gateway", "Evangeline", 
            "Richmond City", "Saint Mary", "Saint Louis City", "Mclean", 
            "Union", "Bienville", "Covington City", "Martinsville City", 
            "Claiborne", "King And Queen", "Mclean", "Mcminn", "Prince Georges" 
), state = c("LA", "LA", "GA", "LA", "IN", "LA", "AK", "LA", "VA", 
      "LA", "MO", "KY", "LA", "LA", "VA", "VA", "LA", "VA", "ND", "TN", 
      "MD")), .Names = c("county", "state"), class = c("tbl_df", "data.frame" 
      ), row.names = c(NA, -21L)) 

       county state 
1   Beauregard LA 
2   De Soto LA 
3    Dekalb GA 
4   Webster LA 
5  Saint Joseph IN 
6  West Feliciana LA 
7 Ketchikan Gateway AK 
8   Evangeline LA 
9  Richmond City VA 
10  Saint Mary LA 
11 Saint Louis City MO 
12   Mclean KY 
13    Union LA 
14   Bienville LA 
15 Covington City VA 
16 Martinsville City VA 
17   Claiborne LA 
18 King And Queen VA 
19   Mclean ND 
20   Mcminn TN 
21 Prince Georges MD 

我用adist創建約80%我縣匹配在fips縣名的模糊字符串匹配算法。然而,有時它會匹配兩個拼寫相似的縣,但來自不同的州(例如,「韋伯斯特,洛杉磯」匹配「韋伯斯特,喬治亞州」而不是「韋伯斯特帕裏什,洛杉磯」)。

distance <- adist(tomatch$county, 
        fips$countyname, 
        partial = TRUE) 


min.name <- apply(distance, 1, min) 

matchedcounties <- NULL 

for(i in 1:nrow(distance)) { 

    s2.i <- match(min.name[i], distance[i, ]) 
    s1.i <- i 

    matchedcounties <- rbind(data.frame(s2.i = s2.i, 
             s1.i = s1.i, 
             s1name = tomatch[s1.i, ]$county, 
             s2name = fips[s2.i, ]$countyname, 
             adist = min.name[i]), 
          matchedcounties) 

} 

因此,我想限制縣城的模糊字符串匹配的拼寫正確版本相匹配的狀態。

我目前的算法使一個大矩陣計算兩個源之間的標準Levenshtein距離,然後選擇最小距離的值。

爲了解決我的問題,我猜我需要創建一個函數,可以應用於每個'國家'組ddply,但我很困惑,我應該如何表明組值ddply函數應該匹配另一個數據幀。使用任何其他軟件包的dplyr解決方案或解決方案也將受到讚賞。

代碼來創建FIPS數據集:

download.file('http://www2.census.gov/geo/docs/reference/codes/files/national_county.txt', 
       './nationalfips.txt') 

fips <- read.csv('./nationalfips.txt', 
       stringsAsFactors = FALSE, colClasses = 'character', header = FALSE) 
names(fips) <- c('state', 'statefips', 'countyfips', 'countyname', 'classfips') 

# remove 'County' from countyname 
fips$countyname <- sub('County', '', fips$countyname, fixed = TRUE) 
fips$countyname <- stringr::str_trim(fips$countyname) 
+2

您的問題將從[可重現的示例]中受益匪淺(http://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example) – MrFlick

回答

2

這是dplyr的一種方法。我第一次由國家與FIPS名參加tomatch data.frame(只允許在狀態相匹配):

require(dplyr) 
df <- tomatch %>% 
    left_join(fips, by="state") 

接下來,我注意到很多縣沒有「聖」,而是「聖「在FIPS數據集中。首先清理起來應該會改善所獲得的結果。

df <- df %>% 
    mutate(county_clean = gsub("Saint", "St.", county)) 

然後,這組由data.frame縣,和計算與adist的距離:

df <- df %>% 
    group_by(county_clean) %>%    # Calculate the distance per county 
    mutate(dist = diag(adist(county_clean, countyname, partial=TRUE))) %>% 
    arrange(county, dist) # Used this for visual inspection. 

注意,我把對角線從所得矩陣作爲adist返回N×M矩陣,其中n表示x矢量和m表示y矢量(它計算所有組合)。 可選,您可以添加AGREP結果:

df <- df %>% 
    rowwise() %>% # 'group_by' a single row. 
    mutate(agrep_result = agrepl(county_clean, countyname, max.distance = 0.3)) %>% 
    ungroup() # Always a good idea to remove 'groups' after you're done. 

然後像之前過濾,取最小間距:

df <- df %>% 
    group_by(county_clean) %>% # Causes it to calculate the 'min' per group 
    filter(dist == min(dist)) %>% 
    ungroup() 

注意,這可能會導致多行返回各自的輸入行在tomatch
或者,做這一切在一次運行(我通常會改變代碼,這種格式一次我相信它做什麼它應該做的):

df <- tomatch %>% 
    # Join on all names in the relevant state and clean 'St.' 
    left_join(fips, by="state") %>% 
    mutate(county_clean = gsub("Saint", "St.", county)) %>% 

    # Calculate the distances, per original county name. 
    group_by(county_clean) %>%     
    mutate(dist = diag(adist(county_clean, countyname, partial=TRUE))) %>% 

    # Append the agrepl result 
    rowwise() %>% 
    mutate(string_agrep = agrepl(county_clean, countyname, max.distance = 0.3)) %>% 
    ungroup() %>% 

    # Only retain minimum distances 
    group_by(county_clean) %>% 
    filter(dist == min(dist)) 

結果在這兩種情況下:

   county  county_clean state    countyname dist string_agrep 
1   Beauregard  Beauregard LA   Beauregard Parish 0   TRUE 
2   De Soto   De Soto LA   De Soto Parish 0   TRUE 
3    Dekalb   Dekalb GA     DeKalb 1   TRUE 
4   Webster   Webster LA   Webster Parish 0   TRUE 
5  Saint Joseph  St. Joseph IN    St. Joseph 0   TRUE 
6  West Feliciana West Feliciana LA  West Feliciana Parish 0   TRUE 
7 Ketchikan Gateway Ketchikan Gateway AK Ketchikan Gateway Borough 0   TRUE 
8   Evangeline  Evangeline LA   Evangeline Parish 0   TRUE 
9  Richmond City  Richmond City VA    Richmond city 1   TRUE 
10  Saint Mary   St. Mary LA   St. Mary Parish 0   TRUE 
11 Saint Louis City St. Louis City MO   St. Louis city 1   TRUE 
12   Mclean   Mclean KY     McLean 1   TRUE 
13    Union    Union LA    Union Parish 0   TRUE 
14   Bienville   Bienville LA   Bienville Parish 0   TRUE 
15 Covington City Covington City VA   Covington city 1   TRUE 
16 Martinsville City Martinsville City VA   Martinsville city 1   TRUE 
17   Claiborne   Claiborne LA   Claiborne Parish 0   TRUE 
18 King And Queen King And Queen VA   King and Queen 1   TRUE 
19   Mclean   Mclean ND     McLean 1   TRUE 
20   Mcminn   Mcminn TN     McMinn 1   TRUE 
21 Prince Georges Prince Georges MD   Prince George's 1   TRU 
+0

非常感謝您的深入解答!完美地處理了我的樣本數據和完整的數據集!簡直不敢相信我那麼堅持的步驟就像一個國家的left_join一樣簡單。再次感謝。 – mcjudd

+0

這真的很有幫助,謝謝! – chandler

1

不要有示例數據,但使用AGREP代替adist和僅搜索名字在該州

sapply(df_tomatch$county, function(x) agrep(x,df_matchby[df_matchby$state==dj_tomatch[x,'state'],'county'],value=TRUE) 

您可以使用試一下max.distance參數agrep來改變它們需要匹配的距離。此外,設置value=TRUE將返回匹配的字符串的值,而不是匹配的位置。

+0

嗨@cole,不幸的是,工作。我試圖在你的'sapply'函數中理解'agrep'的第二個參數。當然,看起來df_tomatch $ county的每個元素都被設置爲要匹配的模式,但我不理解使用'tomatch [x,'state']'作爲行索引。謝謝。 – mcjudd

+0

@mcjudd第二個參數是正在搜索模式的字符串,所以它在'df_matchby'中尋找'county',但是我只是將'df_tomatch $ state'的'與'df_matchby $ state'相同。這樣,縣字符串的每個值只會在共享相同州名的正確縣名的子集中進行搜索。 – cole

+1

@mcjudd我只是用你的數據試過了,發現了我的錯誤。下面應該工作:'sapply(1:nrow(tomatch),function(x)agrep(tomatch [x,'county'],fips [fips $ state == tomatch [x,'state'],'countyname']] ,值= TRUE,最大距離= 0.3))'。這將爲每場比賽吐出一個縣名單,你可以提取第一個,這是最好的比賽。你可以微調'max.distance'來給你最好的結果。 – cole