对行使用带有重复标识符的扩展

Posted

技术标签:

【中文标题】对行使用带有重复标识符的扩展【英文标题】:Using spread with duplicate identifiers for rows 【发布时间】:2016-12-27 11:10:08 【问题描述】:

我有一个长表单数据框,其中包含相同日期和人员的多个条目。

jj <- data.frame(month=rep(1:3,4),
             student=rep(c("Amy", "Bob"), each=6),
             A=c(9, 7, 6, 8, 6, 9, 3, 2, 1, 5, 6, 5),
             B=c(6, 7, 8, 5, 6, 7, 5, 4, 6, 3, 1, 5))

我想把它转换成宽格式,变成这样:

month Amy.A Bob.A Amy.B Bob.B
1     
2     
3
1
2
3
1
2
3
1
2
3

我的问题与this 非常相似。我在答案中使用了给定的代码:

kk <- jj %>% 
  gather(variable, value, -(month:student)) %>% 
  unite(temp, student, variable) %>% 
  spread(temp, value)

但它给出了以下错误:

错误:行 (1, 4), (2, 5), (3, 6), (13, 16), (14, 17), (15, 18), (7, 10) 的标识符重复, (8, 11), (9, 12), (19, 22), (20, 23), (21, 24)

提前致谢。 注意:我不想删除多个条目。

【问题讨论】:

输出没有意义。 Bob.B 5 6 7 怎么样。第 1 个月有两个 Bob B 值,5 3?第 2 个月是4 and 2。最后,第 3 个月 6 5。您将这些总结为一个值。 【参考方案1】:

问题在于AB 的两列。如果我们可以创建一个值列,我们可以按照您的意愿传播数据。当您使用以下代码时,请查看jj_melt 的输出。

library(reshape2)
jj_melt <- melt(jj, id=c("month", "student"))
jj_spread <- dcast(jj_melt, month ~ student + variable, value.var="value", fun=sum)
#   month Amy_A Amy_B Bob_A Bob_B
# 1     1    17    11     8     8
# 2     2    13    13     8     5
# 3     3    15    15     6    11

我不会将此标记为重复,因为sum 没有对另一个问题进行总结,但data.table 的答案可以帮助解决一个额外的论点fun=sum

library(data.table)
dcast(setDT(jj), month ~ student, value.var=c("A", "B"), fun=sum)
#    month A_sum_Amy A_sum_Bob B_sum_Amy B_sum_Bob
# 1:     1        17         8        11         8
# 2:     2        13         8        13         5
# 3:     3        15         6        15        11

如果您想使用tidyr 解决方案,请将其与dcast 结合使用sum 进行汇总。

as.data.frame(jj)
library(tidyr)
jj %>% 
  gather(variable, value, -(month:student)) %>%
  unite(temp, student, variable) %>%
  dcast(month ~ temp, fun=sum)
#   month Amy_A Amy_B Bob_A Bob_B
# 1     1    17    11     8     8
# 2     2    13    13     8     5
# 3     3    15    15     6    11

编辑

根据您的新要求,我添加了一个活动栏。

library(dplyr)
jj %>% group_by(month, student) %>% 
  mutate(id=1:n()) %>%
  melt(id=c("month", "id", "student")) %>%
  dcast(... ~ student + variable, value.var="value")
#   month id Amy_A Amy_B Bob_A Bob_B
# 1     1  1     9     6     3     5
# 2     1  2     8     5     5     3
# 3     2  1     7     7     2     4
# 4     2  2     6     6     6     1
# 5     3  1     6     8     1     6
# 6     3  2     9     7     5     5

也可以使用其他解决方案。这里我添加了一个可选表达式来按活动编号排列最终输出:

library(tidyr)
jj %>% 
  gather(variable, value, -(month:student)) %>%
  unite(temp, student, variable) %>%
  group_by(temp) %>%
  mutate(id=1:n()) %>%
  dcast(... ~ temp) %>%
  arrange(id)
#   month id Amy_A Amy_B Bob_A Bob_B
# 1     1  1     9     6     3     5
# 2     2  2     7     7     2     4
# 3     3  3     6     8     1     6
# 4     1  4     8     5     5     3
# 5     2  5     6     6     6     1
# 6     3  6     9     7     5     5

data.table 语法很紧凑,因为它允许多个value.var 列,并且会为我们处理传播。然后我们可以跳过melt -&gt; cast 进程。

library(data.table)
setDT(jj)[, activityID := rowid(student)]
dcast(jj, ... ~ student, value.var=c("A", "B"))
#    month activityID A_Amy A_Bob B_Amy B_Bob
# 1:     1          1     9     3     6     5
# 2:     1          4     8     5     5     3
# 3:     2          2     7     2     7     4
# 4:     2          5     6     6     6     1
# 5:     3          3     6     1     8     6
# 6:     3          6     9     5     7     5

【讨论】:

感谢您的回答。我不想总结。不需要算术运算。我想为 Amy 创建 A 和 B 列,为 Bob 创建 A 和 B 列,它们只是具有各自的值。 如果同一月份有两个值,学生和班级,您要选择哪一个? 我都想要。实际上,我正在处理出价和要价数据,因此有多个条目。 所以你没有通过有问题的变量进行总结。您需要一个可以充当活动 ID 的新变量。另外,不要在您的问题中使用代码 sn-ps。它们不起作用,输出混乱。只需突出显示您的代码,粘贴、突出显示并使用 Ctrl+K 缩进为代码可读格式。 我们可以使用dcast(month + id ~ temp, value.var="value")。我们使用了一些技巧来缩短它。三个点(所有其他列)使我们不必写month + id,也不必写value.var="value",因为函数将使用最后一列猜测值列。【参考方案2】:

您的答案缺少 mutate id!这是仅使用 dplyr packge 的解决方案。

jj %>% 
  gather(variable, value, -(month:student)) %>% 
  unite(temp, student, variable) %>% 
  group_by(temp) %>% 
  mutate(id=1:n()) %>% 
  spread(temp, value) 
#  A tibble: 6 x 6
#  month    id Amy_A Amy_B Bob_A Bob_B
# * <int> <int> <dbl> <dbl> <dbl> <dbl>
# 1     1     1     9     6     3     5
# 2     1     4     8     5     5     3
# 3     2     2     7     7     2     4
# 4     2     5     6     6     6     1
# 5     3     3     6     8     1     6
# 6     3     6     9     7     5     5

【讨论】:

如果你不想要 id 列,只需在末尾添加%&gt;% select(-id)即可。 这是个好技巧。简要扩展:如果您在收集之前没有每行的唯一 id,则无法确定传播时哪些值属于哪些观察值。添加任何充当主键的列可以缓解这种情况。 这里不只使用dplyrgatherunitespread都来自tidyr【参考方案3】:

因为 tidyr 1.0.0 pivot_widerspread 的推荐替代品,您可以执行以下操作:

jj <- data.frame(month=rep(1:3,4),
                 student=rep(c("Amy", "Bob"), each=6),
                 A=c(9, 7, 6, 8, 6, 9, 3, 2, 1, 5, 6, 5),
                 B=c(6, 7, 8, 5, 6, 7, 5, 4, 6, 3, 1, 5))

library(tidyr)

pivot_wider(
  jj,
  names_from = "student",
  values_from = c("A","B"),
  names_sep = ".",
  values_fn = list(A= list, B= list)) %>%
  unchop(everything())
#> # A tibble: 6 x 5
#>   month A.Amy A.Bob B.Amy B.Bob
#>   <int> <dbl> <dbl> <dbl> <dbl>
#> 1     1     9     3     6     5
#> 2     1     8     5     5     3
#> 3     2     7     2     7     4
#> 4     2     6     6     6     1
#> 5     3     6     1     8     6
#> 6     3     9     5     7     5

由reprex package (v0.3.0) 于 2019-09-14 创建

这个问题的转折是那个月份不是学生独有的,要解决这个问题:

values_fn = list(A= list, B= list)) 将多个值放在一个列表中 unchop(everything()) 垂直取消嵌套列表,您也可以在这里使用unnest

【讨论】:

【参考方案4】:

如果我们创建一个唯一的序列,那么我们可以使用pivot_wider以正确的格式输出

library(dplyr)
library(tidyr)
jj %>%
   group_by(month, student) %>% 
   mutate(rn = row_number()) %>%
   pivot_wider(names_from = 'student', values_from = c('A', 'B'), 
          names_sep='.')  %>% 
   select(-rn)
# A tibble: 6 x 5
# Groups:   month [3]
#  month A.Amy A.Bob B.Amy B.Bob
#  <int> <dbl> <dbl> <dbl> <dbl>
#1     1     9     3     6     5
#2     2     7     2     7     4
#3     3     6     1     8     6
#4     1     8     5     5     3
#5     2     6     6     6     1
#6     3     9     5     7     5

数据

jj <- structure(list(month = c(1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 
1L, 2L, 3L), student = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 2L, 
2L, 2L, 2L, 2L, 2L), .Label = c("Amy", "Bob"), class = "factor"), 
    A = c(9, 7, 6, 8, 6, 9, 3, 2, 1, 5, 6, 5), B = c(6, 7, 8, 
    5, 6, 7, 5, 4, 6, 3, 1, 5)), class = "data.frame", row.names = c(NA, 
-12L))

【讨论】:

以上是关于对行使用带有重复标识符的扩展的主要内容,如果未能解决你的问题,请参考以下文章

创建带有外键的表会出现错误 ORA-00904: : oracle 10g 中的无效标识符 [重复]

带有重复变量的sql pivot

如何使用 Firebase 发送带有类别的通知 [重复]

我使用 id 作为标识符来查找元素,但异常堆栈跟踪显示 css 选择器 [重复]

Angular2 和 TSD 的“重复标识符”错误

删除应用扩展后仍然出现应用安装失败错误 - 此应用包含具有非法捆绑标识符的应用扩展