如何在R语言中重塑指定结构的data.frame?
Hey there! Let's sort out that data reshaping task you've got in R. First, let's start with your original data setup so we're working from the same place:
df <- data.frame( custid = c(1,2,3,4), prod1 = c('jeans','tshirt','jacket','tshirt'), prod1_hnode1 = c(1,2,3,2), prod1_hnode2 = c(6,7,8,7), prod2 = c('tshirt','jeans','jacket','shirt'), prod2_hnode1 = c(2,1,3,4), prod2_hnode2 = c(7,6,8,7) )
Which gives you this wide-format output:
custid prod1 prod1_hnode1 prod1_hnode2 prod2 prod2_hnode1 prod2_hnode2 1 1 jeans 1 6 tshirt 2 7 2 2 tshirt 2 7 jeans 1 6 3 3 jacket 3 8 jacket 3 8 4 4 tshirt 2 7 shirt 4 7
Modern Solution with tidyr::pivot_longer
The cleanest and most intuitive way to get your desired long format is using the pivot_longer function from the tidyr package (part of the popular tidyverse suite). Here's the code that will produce exactly the structure you want:
# Load the tidyverse package (or just tidyr if you prefer) library(tidyverse) # Reshape the data long_df <- df %>% pivot_longer( cols = -custid, # Keep custid as our identifier column (doesn't get reshaped) names_to = c("prod", ".value"), # Split column names into two parts: prod label, and value type names_pattern = "(prod\\d)(?:_(.*))?" # Regex to match our column name patterns ) %>% rename(rec = prod1) %>% # Rename the product name column to match your desired output select(custid, prod, rec, hnode1, hnode2) # Reorder columns to match your target structure # Check the result print(long_df)
Running this will give you:
custid prod rec hnode1 hnode2 <dbl> <chr> <chr> <dbl> <dbl> 1 1 prod1 jeans 1 6 2 1 prod2 tshirt 2 7 3 2 prod1 tshirt 2 7 4 2 prod2 jeans 1 6 5 3 prod1 jacket 3 8 6 3 prod2 jacket 3 8 7 4 prod1 tshirt 2 7 8 4 prod2 shirt 4 7
Let's break down what each part does:
cols = -custid: We excludecustidfrom reshaping because we want one row per customer-product pair, keepingcustidas the identifier.names_to = c("prod", ".value"): This tellspivot_longerto split each column name into two components:prod: This will hold the prefix likeprod1orprod2..value: This special keyword means "use this part of the column name as the new column headers" (sohnode1,hnode2, and the product name column).
names_pattern = "(prod\\d)(?:_(.*))?": The regex handles two types of column names:- For columns like
prod1_hnode1: It capturesprod1into theprodgroup andhnode1into the.valuegroup (becomes a column header). - For columns like
prod1: It capturesprod1into theprodgroup, and the second group is empty—so.valueusesprod1as the column name (which we later rename torec).
- For columns like
rename(rec = prod1): Renames the product name column to match your desiredrecheader.select(...): Reorders the columns to exactly match the structure you provided.
Alternative: Older Method with reshape2::melt
If you're more comfortable with the older reshape2 package, you can achieve the same result with a bit more cleanup:
library(reshape2) # First melt the data into a longer format melted_df <- melt(df, id.vars = "custid") # Split column names into prod and attribute, then clean up melted_df <- melted_df %>% separate(variable, into = c("prod", "attribute"), sep = "_", fill = "right") %>% mutate(attribute = ifelse(is.na(attribute), "rec", attribute)) %>% # Label product name columns as 'rec' pivot_wider(names_from = attribute, values_from = value) %>% # Pivot back to wide for attributes mutate(across(c(hnode1, hnode2), as.numeric)) %>% # Convert hnode columns back to numeric arrange(custid, prod) %>% # Sort to match target output select(custid, prod, rec, hnode1, hnode2) # Reorder columns
This will also give you the exact structure you want, but pivot_longer is more efficient and straightforward for this type of reshaping.
内容的提问来源于stack exchange,提问作者tushaR

