首页 > 解决方案 > 使用 tidyverse 根据索引链接的第三列中的值从另一列生成新列输入值

问题描述

我有一个看起来像这样的数据框(但有数百列):

df_in <- 
  data.frame(
    x_1 = 0:3,
    x_2 = 30:33,
    x_3 = 20:23,
    y_1 = c("a", "c", "b", "c"),
    y_2 = c("b", "a", "c", "b"),
    y_3 = c("c", "b", "a", "a"))

假设 y 列包含运动队的名称(a、b、c);x 变量,通过列索引号与 y 变量相关联,是球队进球数;每一行代表一个 3 路比赛。

对于每场比赛,我想创建新的列来记录两个团队 a 和 b 的表现。这是我想要的输出,使用长期方法:

df_in %>% 
  mutate(
    a_value = 
      case_when(
        y_1 == "a" ~ x_1,
        y_2 == "a" ~ x_2,
        y_3 == "a" ~ x_3),
    b_value = 
      case_when(
        y_1 == "b" ~ x_1,
        y_2 == "b" ~ x_2,
        y_3 == "b" ~ x_3))

鉴于实际数据中有更多列,我需要使其更简洁。我猜有一种 tidyverse 方法可以做到这一点,但我不确定。

标签: rdplyr

解决方案


乍一看比我想象的要复杂一些。我给每个匹配一个 ID,这样你就可以(没有双关语)根据原始行匹配值。第一步是重塑,以便获得一个变量(x 或 y)和一个索引(1、2、3、...)。重塑回宽会让你得到你的a_valueb_value列(我没有放弃团队 c 的观察,因为你说这需要扩展)。然后通过匹配将其连接回原始数据帧。您可以一步完成所有连接(类似于 SQL 子查询),但这有点混乱。

library(tidyr)
df_in <- tibble::rowid_to_column(df_in, "match")
team_vals <- df_in %>%
  pivot_longer(-match, names_to = c(".value", "index"), names_pattern = "(^[a-z])_(\\d$)") %>%
  pivot_wider(id_cols = match, names_from = y, values_from = x, names_glue = "{y}_value")
team_vals
#> # A tibble: 4 × 4
#>   match a_value b_value c_value
#>   <int>   <int>   <int>   <int>
#> 1     1       0      30      20
#> 2     2      31      21       1
#> 3     3      22       2      32
#> 4     4      23      33       3

dplyr::left_join(df_in, team_vals, by = "match")
#>   match x_1 x_2 x_3 y_1 y_2 y_3 a_value b_value c_value
#> 1     1   0  30  20   a   b   c       0      30      20
#> 2     2   1  31  21   c   a   b      31      21       1
#> 3     3   2  32  22   b   c   a      22       2      32
#> 4     4   3  33  23   c   b   a      23      33       3

推荐阅读