【问题标题】:Applying dcast in a weird way以一种奇怪的方式应用 dcast
【发布时间】:2017-01-17 17:01:02
【问题描述】:

我想根据“前导”变量来分隔变量。 x3 在以下情况下:

set.seed(2)
df = data.frame(x1 = sample(4), x2 = sample(4), x3 = sample(letters[1:2], size = 4, replace = TRUE))
df
#   x1 x2 x3
# 1  1  4  a
# 2  3  3  b
# 3  2  1  b
# 4  4  2  a

# Desired output
# x3 x1.a x2.a x1.b x2.b
#  a    1    4   NA   NA
#  b   NA   NA    3    3
#  b   NA   NA    2    1
#  a    4    2   NA   NA

我不知何故觉得这可以通过reshape2::dcast() 实现,但我只能让它对两个变量起作用:

reshape2::dcast(df[,2:3], seq_along(x3) ~ x3, value.var = "x2")[, -1]
#    a  b
# 1  2 NA
# 2 NA  1
# 3 NA  3
# 4  4 NA

但这可能只是对dcast的完全滥用。这个问题有没有优雅的解决方案,无需拆分和合并df?


编辑: 有人提到这样做是一个可怕的想法,我可能不应该这样做。让我详细说明什么时候这才有意义。

想象x3 是一个特定算法的开关。在这种情况下,a 和 b 是选项。此外,x1 和 x2 是两种算法都可以采用的参数。不幸的是,这两种算法在 x1 和 x2 的相同参数设置上的行为确实不同,因此将它们作为不同的特征来处理以考虑它们的不相关性是有意义的。

【问题讨论】:

  • 嗯,有merge(df[df$x3=="a",], df[df$x3=="b",], by="x3", all=TRUE),但自然不会有优雅的方式来获得如此混乱的输出。
  • @agenis 现在已修复。
  • @Frank 如果我现在不知道 x3 的离散值,这会变得很混乱。
  • 只有一句话:如果您想要更多“关注”,您可能应该将问题的标题改进为更具体;下次可能会节省赏金! ;-)
  • 行列顺序重要吗?

标签: r reshape2 dcast


【解决方案1】:

这是使用X3 创建虚拟交互项的解决方案。使用dplyr 或data.table 可能可以将所有这些代码放入一行中,但这里是:

temp <- model.matrix( ~ (x1+x2):x3-1, df)
temp[model.matrix( ~ (I(x1+1)+I(x2+1)):x3-1, df) == temp] <- NA
data.frame(df$x3, temp)
####  df.x3 x1.x3a x1.x3b x3a.x2 x3b.x2
####      a      1     NA      4     NA
####      b     NA      3     NA      3
####      b     NA      2     NA      1
####      a      4     NA      2     NA

列的最终名称和顺序与您略有不同。

注意:(第二行代码的用途)。 model.matrix 函数创建零而不是 NAs,因此无法区分与预先存在的零的区别。第二行是仅找到最终 NA 的技巧(它通过创建第二个模型矩阵同时通过 +1 更改其值来工作)。

【讨论】:

  • 我喜欢你的 model.matrix 方法。我想知道这是否更清楚(尽管速度较慢):m &lt;- model.matrix(~ 0 + x3, df) ; m[m==0] &lt;- NA ; data.frame(df$x3, apply(m, 2, "*", df[-3]))
  • @user20650 感谢您的输入,这确实是一个不错的替代解决方案。您可以将其发布为答案。我想知道我们是否可以将其简化为单列
  • 你也可以使用model.matrix(~ 0 + x3, df)^NA,而不是明确地将零设置为NA。所以在一行data.frame(df$x3, apply(model.matrix(~ 0 + x3, df)^NA , 2, "*", df[-3])),但真的不能称为一行,因为它是几个计算。
  • 这个解决方案唯一困扰我的是,它对于一个通用输入来说有点混乱,我现在只有分类列的名称,因为那时我必须从字符串生成公式。
  • @jakobr 好的,请随时修改您的问题,以便我可以考虑此规范,;-)
【解决方案2】:

这可以使用melt 和dcast 来实现,如果您再添加一列并做一个中介melt。

library(reshape2)
library(magrittr)

set.seed(2)
df = data.frame(x1 = sample(4), x2 = sample(4), x3 = sample(letters[1:2], size = 4, replace = TRUE))

df$row <- 1:nrow(df)

melt(df, 
     id.vars = c("row", "x3"), 
     measure.vars = c("x1", "x2")) %>%
  dcast(row ~ x3 + variable, 
        value.var = "value")

但是,它的运行速度比agenis 的解决方案慢2-3 倍,即使我将数据框的大小推高至10,000 行也是如此。 (8 对 16 毫秒)。

【讨论】:

  • 很好地使用行变量
【解决方案3】:

我自己提出的基本解决方案:

cat.var = "x3"
cont.vars = setdiff(colnames(df), cat.var)
categories = unique(df[[cat.var]])
res = lapply(categories, function(x) {
  this.df = df[, cont.vars, drop = FALSE]
  this.df[df[[cat.var]] != x,] = NA
  setNames(this.df, paste0(x,".",colnames(this.df)))
})
res = do.call(cbind, c(list(df[, cat.var, drop=FALSE]), res))
res

#   x3 a.x1 a.x2 b.x1 b.x2
# 1  a    1    4   NA   NA
# 2  b   NA   NA    3    3
# 3  b   NA   NA    2    1
# 4  a    4    2   NA   NA

【讨论】:

  • 我的时钟速度大约是@agenis 解决方案的 3 倍,是我的 5 倍。做得很好。
【解决方案4】:

你可以使用tidyr

library(tidyr);library(dplyr)
df <- df %>% mutate(rows=rownames(.)) %>% 
gather(., key="vars", value= "val", -x3,-rows) %>%
    mutate(vars= paste(x3,vars, sep=".")) %>%  
spread(., key = vars, value = val) %>%
select(-rows)

它将数据集收集成长格式,分别保存 x3 变量,然后在创建所需的变量标头后,再次传播数据。

【讨论】:

  • 有趣的解决方案,但您必须注意它会重新排序行并按“x3”对它们进行排序。
  • 是的,如果行顺序很重要,您可以在 select 语句之前添加arrange(rows) %&gt;%。但它仍然不是最快的。
猜你喜欢
  • 2017-12-04
  • 1970-01-01
  • 1970-01-01
  • 2019-08-03
  • 2013-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-18
相关资源
最近更新 更多