R根据行中的值在列中重复

时间:2021-04-11 15:22:51

标签: r dataframe repeat long-integer

我有一个如下所示的数据框:

Name    School   Weight Days
Antoine Bach     0.03   5
Antoine Ken      0.02   7
Barbara Franklin 0.04   3

我想获得如下输出:

Name    School   1    2    3    4    5    6    7
Antoine Bach     0.03 0.03 0.03 0.03 0.03 NA   NA
Antoine Ken      0.02 0.02 0.02 0.02 0.02 0.02 0.02
Barbara Franklin 0.04 0.04 0.04 NA   NA   NA   NA

可重现的样本数据:

df <- tribble(
  ~Name,    ~School,   ~Weight, ~Days,
  "Antoine", "Bach",     0.03,   5,
  "Antoine", "Ken",      0.02,   7,
  "Barbara", "Franklin", 0.04,   3
)

5 个答案:

答案 0 :(得分:4)

使用 data.table,您可以通过rep为每行读取 WeightDays 次,然后dcast转换为宽格式来创建长版本以新变量的rowid作为列。

library(data.table)
setDT(df)

dcast(df[, .(rep(Weight, Days)), .(Name, School)], 
      Name + School ~ rowid(V1))

# Name   School    1    2    3    4    5    6    7
# 1: Antoine     Bach 0.03 0.03 0.03 0.03 0.03   NA   NA
# 2: Antoine      Ken 0.02 0.02 0.02 0.02 0.02 0.02 0.02
# 3: Barbara Franklin 0.04 0.04 0.04   NA   NA   NA   NA

您也可以rep Weight Days 的数量,然后重复 NA 次以完成该行。

max_days <- max(df$Days) 

df[, as.list(rep(c(Weight, NA), c(Days, max_days - Days))), 
   .(Name, School)]

# Name   School   V1   V2   V3   V4   V5   V6   V7
# 1: Antoine     Bach 0.03 0.03 0.03 0.03 0.03   NA   NA
# 2: Antoine      Ken 0.02 0.02 0.02 0.02 0.02 0.02 0.02
# 3: Barbara Franklin 0.04 0.04 0.04   NA   NA   NA   NA

答案 1 :(得分:3)

您可以使用 pmap_dfr 跨行应用函数,然后将结果列表行绑定到 tibble 对象中。该函数将参数与列名匹配,其余的行值将在省略号 ... 中捕获。

library(purrr)
library(dplyr)

pmap_dfr(df, function(Weight, Days, ...) c(..., setNames(rep(Weight, Days), 1:Days))) %>% 
  mutate(across(3:last_col(), ~ as.numeric(.)))

因为向量在 R 中是原子的,所以 c() 会将行中的所有内容强制为字符。因此 mutate 将新创建的列转换回数字。

setNames 用于命名新创建的列,需要按行绑定。

输出

  Name    School     `1`   `2`   `3`   `4`   `5`   `6`   `7`
  <chr>   <chr>    <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Antoine Bach      0.03  0.03  0.03  0.03  0.03 NA    NA   
2 Antoine Ken       0.02  0.02  0.02  0.02  0.02  0.02  0.02
3 Barbara Franklin  0.04  0.04  0.04 NA    NA    NA    NA   

注意:pmap_dfr 来自 purrr 包,mutateacrosslast_col 均来自 dplyr

工作原理

当您以上述方式使用 pmap 时,命名函数参数将与具有相同名称的列匹配。因此 WeightsDays 作为函数参数与每一行中同名的列匹配。

... 收集仍然传递给函数但在函数中未使用(按名称)的剩余列。本质上,省略号在您的情况下收集 NameSchool

由于 NameSchool 已经有了名称,因此它们首先被传递给 c() 以保持您的列顺序。此外,我们组合其他值并给它们命名。单行的输出是这样的:

     Name    School         1         2         3         4         5         6 
"Antoine"    "Bach"    "0.03"    "0.03"    "0.03"    "0.03"    "0.03"        NA 
        7 
       NA 

pmap 的输出是一个列表。 _dfr 是将这些列表元素行绑定(因此 r)到数据框/tibble(因此 df)的特定函数。

答案 2 :(得分:2)

您可以使用以下代码获得所需的输出:

library(dplyr)
library(tidyr)

df %>% 
  select(Weight, Days) %>%
  uncount(Days, .remove = FALSE) %>%
  group_by(Days) %>%
  mutate(id = row_number()) %>%
  pivot_wider(Days, names_from = id, values_from = Weight) %>%
  right_join(df, by = "Days") %>%
  relocate(Name, School) %>%
  ungroup() %>%
  select(-c(Weight, Days))

# A tibble: 3 x 9
  Name    School     `1`   `2`   `3`   `4`   `5`   `6`   `7`
  <chr>   <chr>    <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Antoine Bach      0.03  0.03  0.03  0.03  0.03 NA    NA   
2 Antoine Ken       0.02  0.02  0.02  0.02  0.02  0.02  0.02
3 Barbara Franklin  0.04  0.04  0.04 NA    NA    NA    NA 

数据:

df <- tribble(
  ~Name,    ~School,   ~Weight, ~Days,
  "Antoine", "Bach",     0.03,   5,
  "Antoine", "Ken",      0.02,   7,
  "Barbara", "Franklin", 0.04,   3
)

已更新 由于我们亲爱的朋友正确地建议使用 pmap 包中的 mappurrr,这是另一种变体,想知道会很酷:

library(purrr)

df %>%
  mutate(map2_dfr(Weight, Days, ~ set_names(rep(.x, .y), 1:.y))) %>%
  select(-c(Weight, Days))

# A tibble: 3 x 9
  Name    School     `1`   `2`   `3`   `4`   `5`   `6`   `7`
  <chr>   <chr>    <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Antoine Bach      0.03  0.03  0.03  0.03  0.03 NA    NA   
2 Antoine Ken       0.02  0.02  0.02  0.02  0.02  0.02  0.02
3 Barbara Franklin  0.04  0.04  0.04 NA    NA    NA    NA 

答案 3 :(得分:2)

一个 tidyverse 解决方案。

  • 首先我们tidyr::nest 两列。结果列将是一个名为 d 的列表列,表示虚拟。
  • 然后我们使用 d 函数将 weights 变异为 days 的向量,最多 rep 次。此迭代使用 purrr::map 完成。 注意:此阶段不需要 map_dbl,因为它将在下一步中取消嵌套。
  • 我们还将此参数包装到 setNames 中,以便将 d 变异为命名列表(名称如预期)。对于名称,使用 seq 函数。
  • 最后,我们将 tidyr::unnest_widerd 插入到名称已在前面步骤中保存在列表中的列中
library(dplyr)
library(tidyr)
library(purrr)

df %>% nest(d = c(Weight, Days)) %>%
  mutate(d = map(d, ~setNames( rep(.x$Weight, .x$Days), seq(1, .x$Days, 1)))) %>%
  unnest_wider(d)

# A tibble: 3 x 9
  Name    School     `1`   `2`   `3`   `4`   `5`   `6`   `7`
  <chr>   <chr>    <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Antoine Bach      0.03  0.03  0.03  0.03  0.03 NA    NA   
2 Antoine Ken       0.02  0.02  0.02  0.02  0.02  0.02  0.02
3 Barbara Franklin  0.04  0.04  0.04 NA    NA    NA    NA 

答案 4 :(得分:1)

我喜欢 tidyr::uncount 为每行制作 x 份副本。我们可以旋转更长的时间,不计算,然后再次旋转更宽。

library(tidyr)
my_data %>%
  pivot_longer(Weight) %>%
  uncount(Days, .id = "colnum") %>%
  dplyr::select(-name) %>%
  pivot_wider(names_from = colnum, values_from = value)


# A tibble: 3 x 9
  Name    School     `1`   `2`   `3`   `4`   `5`   `6`   `7`
  <chr>   <chr>    <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Antoine Bach      0.03  0.03  0.03  0.03  0.03 NA    NA   
2 Antoine Ken       0.02  0.02  0.02  0.02  0.02  0.02  0.02
3 Barbara Franklin  0.04  0.04  0.04 NA    NA    NA    NA   
相关问题