如何比较行数不同的两个tibble多观测并实现自定义匹配
Alright, let's work through your problem with the tools you're using (dplyr 0.7.4 + R 3.4.1). The goal is to match each value of b in tibble A to any c in tibble C using a custom comparison function, then pull the first matching d value (or NA if no match exists). Here are a few solid approaches, starting with dplyr-focused solutions:
1. dplyr + purrr (Row-wise Processing)
This is the most straightforward dplyr-compatible method for your version. We'll use rowwise() to handle each row of A individually, then use simple logic to check matches in C.
First, let's set up example data and a custom comparison function (you can swap this out for your own logic):
library(dplyr) library(purrr) # Example tibbles set.seed(123) tibble_A <- tibble(b = c("apple", "banana", "cherry", "date", "elderberry")) tibble_C <- tibble(c = c("app", "ban", "cher", "dat", "berry"), d = c("fruit1", "fruit2", "fruit3", "fruit4", "fruit5")) # Custom comparison function: Check if b starts with c (works in R 3.4.1) custom_match <- function(x, y) { substr(x, 1, nchar(y)) == y }
Now apply the logic:
matched_result <- tibble_A %>% rowwise() %>% mutate(d = { # Find all d values in C where the custom match is true matching_ds <- tibble_C$d[custom_match(b, tibble_C$c)] # Return first match if exists, else NA if (length(matching_ds) > 0) matching_ds[1] else NA_character_ }) %>% ungroup() # View the result print(matched_result)
2. Cartesian Product + Filter + Grouped Selection
If you prefer avoiding rowwise() (which can be slower for large datasets), you can generate all possible combinations of A and C, filter for matches, then keep only the first match per row in A. Note that dplyr 0.7.4 doesn't have cross_join(), so we'll use expand.grid() for compatibility:
matched_result_2 <- tibble_A %>% # Add a row ID to track original rows in A mutate(row_id = row_number()) %>% # Generate all combinations of A and C expand.grid(b = .$b, c = tibble_C$c, d = tibble_C$d, row_id = .$row_id) %>% as_tibble() %>% # Filter rows where custom match is true filter(custom_match(b, c)) %>% # Keep only the first match per original row in A group_by(row_id) %>% slice(1) %>% ungroup() %>% # Join back to original A to include rows with no matches (NA for d) right_join(tibble_A %>% mutate(row_id = row_number()), by = c("row_id", "b")) %>% # Clean up columns select(b, d) print(matched_result_2)
3. Alternative: data.table (For Larger Datasets)
If you're working with big data and need faster performance, data.table is a great alternative. It handles row-wise operations efficiently:
library(data.table) # Convert tibbles to data.tables setDT(tibble_A) setDT(tibble_C) # Apply matching logic matched_result_dt <- tibble_A[, d := { matching_ds <- tibble_C$d[custom_match(b, tibble_C$c)] if (length(matching_ds) > 0) matching_ds[1] else NA_character_ }, by = b] print(matched_result_dt)
Custom Function Flexibility
The custom_match() function can be adapted to any logic you need. For example, if you're working with numerical values and want to match within a range:
# Numerical example tibble_A_num <- tibble(b = c(1.2, 3.5, 5.7, 8.1)) tibble_C_num <- tibble(c = c(1, 3, 6, 8), d = c("low", "mid", "high", "very_high")) # Custom function: Match if b is within ±0.5 of c num_match <- function(x, y) { abs(x - y) <= 0.5 } # Apply with the first dplyr method num_result <- tibble_A_num %>% rowwise() %>% mutate(d = { matches <- tibble_C_num$d[num_match(b, tibble_C_num$c)] if (length(matches) > 0) matches[1] else NA_character_ }) %>% ungroup() print(num_result)
内容的提问来源于stack exchange,提问作者mickkk

