You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

第三方DataFrame长字符串解析与按指定列序重组技术问询

解析带标识的餐食字符串并重组DataFrame列序

问题背景

从第三方获取的mealDF中,meals列是包含PR(蛋白质)、FR(水果)、VG(蔬菜)标识的长字符串,每份餐含1个PR、1个FR、2个VG。需要解析后按指定列头[PR, FR, VG1, VG2]重组为目标DataFrame,数据量达数百万行。

现有进展

已通过str_split_fixed拆分出食物项,但无法对应到目标列:

library(stringr)
# 原始数据
id <- c('meal1', 'meal2', 'meal3')
meals <- c(' FR Banana VG Carrot VG Celery PR Chicken ', ' VG Broccoli PR Tofu VG Celery FR Apple ', ' PR Pork VG Brussels Sprouts FR Orange VG Carrot ')
mealDF <- data.frame(id, meals, stringsAsFactors = FALSE)

# 拆分食物项
sepMeals <- as.data.frame(str_split_fixed(mealDF$meals, c(' FR | VG | PR '), 5))[,2:5]

解决方案

核心思路:提取每行的标识序列,将食物项与标识配对后,按目标列名重新映射,同时区分两个VG为VG1、VG2。

步骤1:提取每行的标识序列

用str_extract_all提取每行中的PR/FR/VG,保留出现顺序:

# 提取每行的标识(PR/FR/VG)
tags <- str_extract_all(mealDF$meals, 'PR|FR|VG')
# 转换为数据框,每行对应一个标识序列
tags_df <- as.data.frame(do.call(rbind, tags), stringsAsFactors = FALSE)

步骤2:配对标识与食物项,生成键值对

将标识和拆分后的食物项按行绑定,然后转成长格式,再处理VG的序号:

library(dplyr)
library(tidyr)

# 合并标识和食物项,转为长格式
combined <- bind_cols(tags_df, sepMeals) %>%
  rename_with(~paste0('tag', 1:4), 1:4) %>%
  rename_with(~paste0('food', 1:4), 5:8) %>%
  pivot_longer(cols = everything(),
               names_to = c('.value', 'num'),
               names_pattern = '(tag|food)(\\d)') %>%
  # 给VG添加序号(因为有两个)
  group_by(id = mealDF$id, tag) %>%
  mutate(tag = ifelse(tag == 'VG', paste0(tag, row_number()), tag)) %>%
  ungroup()

步骤3:按目标列名重组为宽格式

# 转宽格式并匹配目标列序
result <- combined %>%
  pivot_wider(names_from = tag, values_from = food) %>%
  select(id, PR, FR, VG1, VG2) %>%
  # 去除字符串前后空格
  mutate(across(c(PR, FR, VG1, VG2), str_trim))

# 查看结果
print(result)

运行后输出符合预期:

id      PR     FR              VG1    VG2
1 meal1 Chicken Banana           Carrot Celery
2 meal2    Tofu  Apple          Broccoli Celery
3 meal3    Pork Orange Brussels Sprouts Carrot

性能优化(针对百万行数据)

  • 优先使用stringr的向量化函数,避免逐行循环
  • 若dplyr/tidyr速度不足,可尝试用data.table替代:
library(data.table)
setDT(mealDF)

# 提取标识和食物项
mealDF[, c(paste0('tag', 1:4), paste0('food', 1:4)) := {
  tags <- str_extract_all(meals, 'PR|FR|VG')[[1]]
  foods <- str_split_fixed(meals, ' FR | VG | PR ', 5)[,2:5]
  c(as.list(tags), as.list(foods))
}, by = id]

# 处理VG序号并转宽格式
long_df <- melt(mealDF, measure.vars = patterns('tag', 'food'),
                value.name = c('tag', 'food'))
long_df[, tag := ifelse(tag == 'VG', paste0(tag, .GRP), tag), by = .(id, tag)]
result_dt <- dcast(long_df, id ~ tag, value.var = 'food')[, .(id, PR, FR, VG1, VG2)]
result_dt[, lapply(.SD, str_trim), .SDcols = c('PR', 'FR', 'VG1', 'VG2')]

内容的提问来源于stack exchange,提问作者CoolGuyHasChillDay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 10:11:07