R中用vapply基于关联表提取字符串遇错,求百万级数据解决方案
问题描述
现有两个data.table:
dfEntitiesCountries:存储3位字符实体与其对应路由国家的关联表temp:约200万条运单数据,包含字段:AWB:运单标识OriginCountry:运单始发国全称Route:运单经过的实体,以连字符分隔
需求:提取每个运单的Gateway——先排除Route中所有对应始发国的前置实体,再取首个路由国家与始发国不同的实体。
编写GetGW函数单独调用正常,但用vapply批量处理temp表时出现错误:
Error in
[.data.table(dfEntitiesCountries[RouteEntities, on = .(Entity = V1), :
i evaluates to a logical vector length 3 but there are 2 rows. Recycling of logical i is no longer allowed as it hides more bugs than is worth the rare convenience. Explicitly use rep(...,length=.N) if you really need to recycle.
原代码:
GetGW <- function(RouteEntities,ShptOrgCnt){ setDT(as.list(RouteEntities)) GW <-dfEntitiesCountries[RouteEntities,on=.(Entity=V1),nomatch=0][RoutingCountry!=ShptOrgCnt,.(Entity)][[1]][1]%>% as.character() } temp[,`:=`(GateWay=vapply(strsplit(Route,"-"),GetGW,"character",ShptOrgCnt=OriginCountry))]
修复方案与高效处理思路
一、原函数错误修复
错误核心是vapply调用时,ShptOrgCnt=OriginCountry会传递整列值而非逐行对应值,导致逻辑向量长度不匹配;同时setDT(as.list(RouteEntities))的写法存在逻辑问题。修改后的函数及调用方式如下:
GetGW <- function(RouteEntities, ShptOrgCnt) { # 将拆分后的实体向量转为标准data.table entity_dt <- data.table(Entity = RouteEntities) # 关联路由国家,过滤始发国实体后取第一个结果 gw_result <- dfEntitiesCountries[entity_dt, on = .(Entity), nomatch = 0][ RoutingCountry != ShptOrgCnt, Entity][1] # 处理无符合条件实体的情况,返回NA as.character(ifelse(length(gw_result) == 0, NA, gw_result)) } # 改用mapply实现逐行参数匹配 temp[, GateWay := mapply(GetGW, strsplit(Route, "-"), OriginCountry, SIMPLIFY = TRUE)]
不过这种逐行处理的方式对200万级数据效率有限,更推荐下面的向量化方案。
二、百万级数据高效处理方案
利用data.table的向量化分组操作,彻底避免逐行循环,大幅提升处理速度:
- 拆分Route字段并展开为长表
将每个运单的Route拆分为多行记录,保留运单标识和始发国信息:
temp_long <- temp[, .(Entity = strsplit(Route, "-")[[1]]), by = .(AWB, OriginCountry)]
- 关联路由国家并过滤目标实体
关联实体-国家表,按运单分组后,筛选出首个非始发国的实体:
# 关联路由国家数据,丢弃无匹配的实体 temp_long <- temp_long[dfEntitiesCountries, on = .(Entity), nomatch = 0] # 标记是否为始发国实体,计算累计非始发国的标识 temp_long[, is_origin := RoutingCountry == OriginCountry] temp_long[, cum_non_origin := cumsum(!is_origin), by = AWB] # 取每个运单中第一个非始发国的实体 gateway_dt <- temp_long[cum_non_origin == 1, .(GateWay = first(Entity)), by = AWB]
- 合并结果回原表
将计算得到的Gateway字段合并到原始运单表:
temp[gateway_dt, on = .(AWB), GateWay := i.GateWay]
内容的提问来源于stack exchange,提问作者Mohammed Barakat
相关产品推荐
相关产品推荐

