首页 > 解决方案 > R - mgsub 问题:被替换的子字符串不是整个字符串

问题描述

我已经从 USPS 下载了街道缩写。这是数据:

dput(usps_streets)
structure(list(common_abbrev = c("allee", "alley", "ally", "aly", 
"anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
"aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
"bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
"bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
"boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
"brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
"byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
"cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
"centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
"circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
"cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
"cor", "corner", "corners", "cors", "course", "crse", "court", 
"ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
"crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
"xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
"dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
"drives", "est", "estate", "estates", "ests", "exp", "expr", 
"express", "expressway", "expw", "expy", "ext", "extension", 
"extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
"fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
"flts", "ford", "frd", "fords", "forest", "forests", "frst", 
"forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
"fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
"garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
"gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
"glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
"harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
"hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
"hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
"holw", "holws", "inlt", "is", "island", "islnd", "islands", 
"islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
"junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
"keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
"lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
"ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
"lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
"mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
"meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
"motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
"mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
"orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
"prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
"parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
"pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
"plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
"port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
"radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
"rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
"rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
"rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
"shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
"shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
"spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
"sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
"statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
"strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
"st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
"ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
"track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
"trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
"tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
"turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
"vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
"view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
"villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
"vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
"way", "ways", "well", "wells", "wls"), usps_abbrev = c("aly", 
"aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
"ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
"bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
"btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
"br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
"byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
"cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
"ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
"cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
"clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
"ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
"cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
"xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
"dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
"expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
"ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
"fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
"frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
"frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
"fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
"gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
"glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
"hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
"hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
"hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
"is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
"jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
"kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
"lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
"lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
"ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
"mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
"mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
"mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
"orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
"pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
"psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
"pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
"pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
"pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
"rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
"rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
"rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
"shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
"skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
"spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
"sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
"stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
"sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
"trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
"trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
"tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
"tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
"vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
"vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
"vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
"wall", "way", "way", "ways", "wl", "wls", "wls")), class = "data.frame", row.names = c(NA, 
-503L))

我想用它们来处理街道地址和州。玩具数据:

a <- c("10900 harper ave", "12235 davis annex", "24 van cortland parkway")

为了将常用缩写转换为 usps 缩写(标准化数据),我构建了一个小函数:

mr_zip <- function(x){
  x <-textclean::mgsub(usps_streets$common_abbrev, usps_streets$usps_abbrev, x, fixed = T,
                   order.pattern = T)
  return(x)
}

当我将函数应用于我的数据时,就会出现问题:

f <- sapply(a, mr_zip)

我得到错误的结果:

 "10900 harper avee"       "1235 davis anx" "24 van cortland pkway"

因为我应该得到的是:

"10900 harper ave"       "1235 davis anx" "24 van cortland pkwy"

我的问题:

  1. 当我在函数中指定order.pattern = T时为什么会发生这种情况?fixed = Tmgsub
  2. 我能做些什么来解决它?
  3. 是否有另一种方法可以在文本的多种替换模式中使用向量?

在此先感谢,欢迎所有建议。

编辑:感谢@RichieSacramento,我发现使用单词边界确实有帮助,但在大型数据帧(> 400,000 行)上使用该函数仍然非常慢。使用safe = TRUEinmgsub会导致该功能正常工作,但速度非常慢。需要快速的东西——因此是赏金。

标签: rstringperformancegsub

解决方案


所以让我们开始玩吧。

第 1 步 首先,我们将您的数据加载到一个tibble名为USPS.

library(tidyverse)
USPS = tibble(
 common_abbrev = c("allee", "alley", "ally", "aly", 
 "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
 "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
 "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
 "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
 "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
 "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
 "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
 "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
 "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
 "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
 "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
 "cor", "corner", "corners", "cors", "course", "crse", "court", 
 "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
 "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
 "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
 "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
 "drives", "est", "estate", "estates", "ests", "exp", "expr", 
 "express", "expressway", "expw", "expy", "ext", "extension", 
 "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
 "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
 "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
 "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
 "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
 "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
 "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
 "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
 "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
 "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
 "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
 "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
 "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
 "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
 "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
 "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
 "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
 "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
 "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
 "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
 "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
 "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
 "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
 "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
 "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
 "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
 "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
 "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
 "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
 "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
 "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
 "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
 "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
 "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
 "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
 "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
 "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
 "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
 "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
 "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
 "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
 "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
 "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
 "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
 "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
 "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
 "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
 "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
 "way", "ways", "well", "wells", "wls"), 
 usps_abbrev = c("aly", 
 "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
 "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
 "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
 "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
 "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
 "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
 "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
 "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
 "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
 "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
 "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
 "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
 "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
 "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
 "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
 "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
 "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
 "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
 "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
 "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
 "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
 "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
 "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
 "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
 "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
 "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
 "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
 "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
 "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
 "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
 "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
 "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
 "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
 "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
 "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
 "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
 "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
 "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
 "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
 "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
 "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
 "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
 "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
 "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
 "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
 "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
 "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
 "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
 "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
 "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
 "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
 "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
 "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
 "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
 "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
 "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
 "wall", "way", "way", "ways", "wl", "wls", "wls"))

USPS

输出

# A tibble: 503 x 2
   common_abbrev usps_abbrev
   <chr>         <chr>      
 1 allee         aly        
 2 alley         aly        
 3 ally          aly        
 4 aly           aly        
 5 anex          anx        
 6 annex         anx        
 7 annx          anx        
 8 anx           anx        
 9 arc           arc        
10 arcade        arc        
# ... with 493 more rows

第 2 步 现在我们将您的USPS表格转换为带有命名元素的向量。

USPSv = array(data = USPS$usps_abbrev, 
              dimnames= list(USPS$common_abbrev))

让我们看看它给了我们什么

USPSv['viadct']
# viadct 
#  "via" 

USPSv['coves'] 
# coves 
# "cvs" 

看起来很诱人。

第 3 步 现在让我们创建一个转换(矢量化)函数,将我们的USPSv矢量与命名元素一起使用。

USPS_conv = function(x) {
  comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
  str_replace(x, comm, USPSv[comm])
}
USPS_conv = Vectorize(USPS_conv)

让我们看看我们的USPS_conv工作原理。

USPS_conv("10900 harper coves")
# 10900 harper coves 
# "10900 harper cvs"

USPS_conv("10900 harper viadct")
# 10900 harper viadct 
# "10900 harper via"

很好,但它会处理向量吗?

USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
# 10900 harper coves 10900 harper viadct    10900 harper ave 
# "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   

到目前为止,一切都很顺利。

第 4 步 现在是时候在USPS_conv函数中使用我们的函数了mutate。但是,我们需要一些输入数据。我们将自己生成它们。

n=10
set.seed(1111)
df = tibble(
  addresses = paste(
    sample(10:10000, n, replace = TRUE),
    sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
    sample(USPS$common_abbrev, n, replace = TRUE)
  )
)
df

输出

# A tibble: 10 x 1
   addresses          
   <chr>              
 1 8995 davis crk     
 2 8527 davis tunnl   
 3 7663 von brown wall
 4 3043 harper lake   
 5 9192 von brown grdn
 6 120 marry rvr      
 7 72 von brown locks 
 8 8752 marry gardn   
 9 7754 davis corner  
10 3745 davis jcts  

让我们执行一个突变

df %>% mutate(addresses = USPS_conv(addresses))

输出

# A tibble: 10 x 1
   addresses          
   <chr>              
 1 8995 davis crk     
 2 8527 davis tunl    
 3 7663 von brown wall
 4 3043 harper lk     
 5 9192 von brown gdn 
 6 120 marry riv      
 7 72 von brown lcks  
 8 8752 marry gdn     
 9 7754 davis cor     
10 3745 davis jcts 

看起来没问题吗?好像是最多的。

第 5 步 现在是时候对 1,000,000 个地址进行一次大测试了!我们将像以前一样生成数据。

n=1000000
set.seed(1111)
df = tibble(
  addresses = paste(
    sample(10:10000, n, replace = TRUE),
    sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
    sample(USPS$common_abbrev, n, replace = TRUE)
  )
)
df

输出

# A tibble: 1,000,000 x 1
   addresses              
   <chr>                  
 1 8995 marry pass        
 2 8527 davis spng        
 3 7663 marry loaf        
 4 3043 davis common      
 5 9192 marry bnd         
 6 120 von brown corner   
 7 72 van cortland plains 
 8 8752 van cortland crcle
 9 7754 von brown sqrs    
10 3745 marry key         
# ... with 999,990 more rows

所以我们走吧。但是让我们立即测量需要多长时间。

start_time =Sys.time()
df %>% mutate(addresses = USPS_conv(addresses))
Sys.time()-start_time
#Time difference of 3.610211 mins

如您所见,我花了不到 4 分钟的时间。我不知道您是否期待更快的东西,以及您是否对这次感到满意。我将等待您的评论。

最后一分钟更新

事实证明,USPS_conv如果我们稍微更改它的代码,可以稍微加快速度。

USPS_conv2 = function(x) {
  t = str_split(x, " ")
  comm = t[[1]][length(t[[1]])]
  str_replace(x, comm, USPSv[comm])
}
USPS_conv2 = Vectorize(USPS_conv2)

USPS_conv2功能的运行速度稍快。

在此处输入图像描述

所有这一切都转化为将一百万条记录的突变时间减少到 3.3 分钟。

超级速度的大更新!

我意识到我的第一个答案版本虽然结构简单,但有点慢:-(。所以我决定想出更快的东西。我会在这里分享我的想法,但请注意,一些解决方案会有点“神奇”。

魔法词典-环境

为了加快操作,我们需要创建一个字典,将键快速转换为值。我们将使用 R 中的环境创建它。

这是我们字典的一个小界面。

#Simple Dictionary (hash Table) Interface for R
ht.create = function() new.env()

ht.insert = function(ht, key, value)  ht[[key]] <- value
ht.insert = Vectorize(ht.insert, c("key", "value"))

ht.lookup = function(ht, key) ht[[key]]
ht.lookup = Vectorize(ht.lookup, "key")

ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
ht.delete = Vectorize(ht.delete, "key")

它是怎么发生的。我已经展示了。下面我将创建一个新的字典环境ht.create(),我将在其中添加两个元素“a1”和“a2” ht.insert,其值分别为“va1”和“va2”。最后,我将向我的环境字典询问这些ht.lookup键的值。

ht1 = ht.create()
ht.insert(ht1, "a1", "va1" )
ht1 %>% ht.insert("a2", "va2")
ht.lookup(ht1, "a1")
# a1
# "va1"
ht1 %>% ht.lookup("a2")
# a2
# "va2"

请注意,函数ht.insert ht.lookup是矢量化的,这意味着我将能够将整个向量添加到字典中。以同样的方式,我将能够通过给出整个向量来查询我的字典。

ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))

ht.lookup(ht1, paste0("a", 10:1))
# a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
# "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
ht1 %>% ht.lookup(paste0("a", 20:11))
# a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
# "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"

魔法属性

现在我们将执行一个函数,该函数将向选定的字典环境表添加一个附加属性。

#Functions that add a dictionary attribute to tibble
addHashTable = function(.data, key, value){
  key = enquo(key)
  value = enquo(value)

  if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
    stop(paste0("`.data` must contain `", as_label(key),
                "` and `", as_label(value), "` columns"))
  }

  if((.data %>% distinct(!!key, !!value) %>% nrow)!=
     (.data %>% distinct(!!key) %>% nrow)){
    warning(paste0(
      "\nThe number of unique values of the ", as_label(key),
      " variable is different\n",
      " from the number of unique values of the ",
      as_label(key), " and ", as_label(value)," pairs!\n",
      "The dictionary will only return the last values for a given key!"))
  }

  ht = ht.create()
  ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                   .data %>% distinct(!!key, !!value) %>% pull(!!value))
  attr(.data, "hashTab") = ht
  .data
}


addHashTable2 = function(.x, .y, key, value){
  key = enquo(key)
  value = enquo(value)

  if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
    stop(paste0("`.y` must contain `", as_label(key),
                "` and `", as_label(value), "` columns"))
  }

  if((.y %>% distinct(!!key, !!value) %>% nrow)!=
     (.y %>% distinct(!!key) %>% nrow)){
    warning(paste0(
      "\nThe number of unique values of the ", as_label(key),
      " variable is different\n",
      " from the number of unique values of the ",
      as_label(key), " and ", as_label(value)," pairs!\n",
      "The dictionary will only return the last values for a given key!"))
  }

  ht = ht.create()
  ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                   .y %>% distinct(!!key, !!value) %>% pull(!!value))
  attr(.x, "hashTab") = ht
  .x
}

那里实际上有两个功能。该addHashTable函数将字典环境属性添加到它从中获取键值对的同一个表中。该addHashTable2函数同样添加到字典环境表,但从另一个表中检索密钥对。

让我们看看是如何addHashTable工作的。

USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
str(USPS)
# tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
# $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
# $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
# - attr(*, "hashTab")=<environment: 0x000000001591bbf0>

如您所见,USPS表中添加了一个指向0x000000001591bbf0环境的属性。

替换功能

我们需要创建一个函数,该函数将使用以这种方式添加的字典环境,在这种情况下,将指定变量中的最后一个单词替换为字典中的相应值。这里是。

replaceString = function(.data, value){
  value = enquo(value)

  #Test whether the value variable is in .data
  if(!(as_label(value) %in% names(.data))){
    stop(paste("The", as_label(value),
               "variable does not exist in the .data table!"))
  }

  #Dictionary attribute presence test
  if(!("hashTab" %in% names(attributes(.data)))) {
    stop(paste0(
      "\nThere is no dictionary attribute in the .data table!\n",
      "Use addHashTable or addHashTable2 to add a dictionary attribute."))
  }

  txt = .data %>% pull(!!value)
  i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
  txt = paste0(str_sub(txt, end=i),
               ht.lookup(attr(.data, "hashTab"),
                         str_sub(txt, start=i+1)))
  .data %>% mutate(!!value := txt)
}

第一次测试

是时候写第一个文本了。为了避免复制代码,我添加了一个小函数,它返回一个包含随机选择的地址的表。

randomAddresses = function(n){
  tibble(
    addresses = paste(
      sample(10:10000, n, replace = TRUE),
      sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
      sample(USPS$common_abbrev, n, replace = TRUE)
    )
  )
}

set.seed(1111)
df = randomAddresses(10)
df
# # A tibble: 10 x 1
#   addresses
#   <chr>
# 1 74 marry forges
# 2 787 von brown knol
# 3 2755 van cortland summit
# 4 9405 harper plaza
# 5 5376 marry pass
# 6 1857 marry trailer
# 7 9810 von brown drv
# 8 7984 davis garden
# 9 9110 marry alley
# 10 6458 von brown row

是时候使用我们神奇的文本替换功能了。但是,请记住首先将字典环境添加到表中。

df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
df %>% replaceString(addresses)
# A tibble: 10 x 1
#   addresses
#   <chr>
# 1 74 marry frgs
# 2 787 von brown knl
# 3 2755 van cortland smt
# 4 9405 harper plz
# 5 5376 marry pass
# 6 1857 marry trlr
# 7 9810 von brown dr
# 8 7984 davis gdn
# 9 9110 marry aly
# 10 6458 von brown row

它看起来像它的作品!

大考验

好吧,没有什么可以等待的。现在让我们在一个有一百万行的表上试试。让我们立即测量绘制地址和添加字典环境需要多长时间。

start_time =Sys.time()
df = randomAddresses(1000000)
df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
Sys.time()-start_time
#Time difference of 1.56609 secs

输出

df
# A tibble: 1,000,000 x 1
#   addresses              
#   <chr>                  
# 1 8995 marry pass        
# 2 8527 davis spng        
# 3 7663 marry loaf        
# 4 3043 davis common      
# 5 9192 marry bnd         
# 6 120 von brown corner   
# 7 72 van cortland plains 
# 8 8752 van cortland crcle
# 9 7754 von brown sqrs    
# 10 3745 marry key         
# # ... with 999,990 more rows

1.6秒可能不算多。但是,最大的问题是替换缩写需要多长时间。

start_time =Sys.time()
df = df %>% replaceString(addresses)
Sys.time()-start_time
#Time difference of 8.316476 secs

输出

# A tibble: 1,000,000 x 1
#   addresses            
#   <chr>                
#   1 8995 marry pass      
# 2 8527 davis spg       
# 3 7663 marry lf        
# 4 3043 davis cmn       
# 5 9192 marry bnd       
# 6 120 von brown cor    
# 7 72 van cortland plns 
# 8 8752 van cortland cir
# 9 7754 von brown sqs   
# 10 3745 marry ky        
# # ... with 999,990 more rows

繁荣!!我们有 8 秒!

我确信在 R 中无法建立更快的机制。

@ThomasIsCoding 的小更新

下面是一个小的基准测试。注意我借用了函数的代码f_MK_conv2 f_TIC1以及f_TIC2来自@ThomasIsCoding。

set.seed(1111)
df = randomAddresses(10000)
df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)

library(microbenchmark)
mb1 = microbenchmark(
  f_MK_conv2(df$addresses),
  f_TIC1(df$addresses),
  f_TIC2(df$addresses),
  replaceString(df, addresses),
  times = 20L
)
ggplot2::autoplot(mb1)

在此处输入图像描述


推荐阅读