合并存在类别不匹配的两个DataFrame:匹配ID与类别并赋值Outcome
Solution for Category Matching & Outcome Lookup
Looks like you're dealing with a common messy data matching problem—no worries, we can solve this efficiently with string manipulation and join logic, even for 500k rows. Here's a step-by-step approach that aligns with your requirements:
Core Logic
We need to handle two types of Category matches:
- Numeric categories: Match pure numbers (e.g., "1") or "Number + Number" (e.g., "Number 1") to the corresponding pure number in
df2. - Fixed text categories: Match exact text (e.g., "Kat 1", "Test") or extract fixed text from messy strings (e.g., pull "Category 5" from "Number 2 - Category 5").
We'll use dplyr for readability (great for smaller datasets) and data.table for speed (critical for 500k rows).
Option 1: Readable dplyr Approach
First, load required packages:
library(dplyr) library(stringr)
- Preprocess
df2into a grouped list for fast lookup by ID:
df2_list <- df2 %>% group_split(ID, .keep = FALSE) %>% setNames(unique(df2$ID))
- Define a custom matching function to handle all cases:
match_outcome <- function(id, cat) { # Get subset of df2 for the current ID df2_sub <- df2_list[[as.character(id)]] if (is.null(df2_sub)) return(NA) # 1. Exact match first exact_match <- df2_sub %>% filter(Category == cat) if (nrow(exact_match) > 0) return(exact_match$Outcome) # 2. Match "Number X" to pure number X num_extract <- str_extract(cat, "Number (\\d+)") if (!is.na(num_extract)) { num <- str_remove(num_extract, "Number ") num_match <- df2_sub %>% filter(Category == num) if (nrow(num_match) > 0) return(num_match$Outcome) } # 3. Extract fixed category from messy strings (e.g., "Category 5" from "Number 2 - Category 5") contains_match <- df2_sub %>% filter(str_detect(cat, Category)) if (nrow(contains_match) > 0) return(contains_match$Outcome[1]) # No matches found return(NA) }
- Apply the function to
df1and keep its original structure:
df1_result <- df1 %>% rowwise() %>% mutate(Outcome = match_outcome(ID, Category)) %>% ungroup()
Option 2: High-Speed data.table Approach
For 500k rows, data.table is significantly faster. Load packages first:
library(data.table) library(stringr) # Convert data frames to data.tables setDT(df1) setDT(df2)
- Create an auxiliary column to extract numbers from "Number X" strings:
df1[, num_col := str_extract(Category, "(?<=Number )\\d+")]
- First, do an exact left join to capture direct matches:
df1_result <- df2[df1, on = .(ID, Category), Outcome := i.Outcome]
- Fill missing Outcomes with numeric matches (pure number ↔ "Number X"):
df1_result[is.na(Outcome) & !is.na(num_col), Outcome := df2[.SD, on = .(ID, Category = num_col), x.Outcome]]
- Fill remaining missing Outcomes by extracting fixed categories from messy strings:
# Pre-group df2's categories by ID for fast access df2_cat_list <- df2[, .(Category_list = list(Category)), by = ID] # Match each remaining row to its fixed category df1_result[is.na(Outcome), Outcome := sapply(1:.N, function(i) { current_id <- .SD$ID[i] current_cat <- .SD$Category[i] available_cats <- df2_cat_list[ID == current_id]$Category_list[[1]] matched_cat <- available_cats[str_detect(current_cat, available_cats)] if (length(matched_cat) > 0) { df2[ID == current_id & Category == matched_cat[1], Outcome] } else { NA_real_ } }), by = ID] # Clean up the auxiliary column df1_result[, num_col := NULL]
Result Preview
Running either approach on your sample data will produce this output (matching your expected outcomes):
> df1_result ID Category Outcome 1: 1 Number 1 1 2: 1 Number 2 2 3: 1 Category 1 4 4: 1 3 3 5: 2 8 NA 6: 2 Number 2 - Category 5 5 7: 2 1 6 8: 3 Number 4 9 9: 3 Kat 1 10 10: 3 4 9 11: 3 Kat 2 11 12: 3 Number5 NA 13: 4 Test 18 14: 4 4 17 15: 4 3 16
Notes
- If you have other numeric prefixes (e.g., "Num 1"), adjust the regex in
str_extractto"(?<=Num(ber)? )\\d+". - If multiple fixed categories match a messy string, the code picks the first one—modify
matched_cat[1]to handle this differently if needed. - The
data.tablemethod is optimized for large datasets and will handle 500k rows smoothly.
内容的提问来源于stack exchange,提问作者WillyWonka
相关产品推荐
相关产品推荐

