anscombe 数据的重塑不那么笨重

less clunky reshaping of anscombe data

我试图使用 ggplot2 绘制 R 中内置的 anscombe 数据集(其中包含四个不同的小数据集,它们具有相同的相关性但 X 和 Y 之间的关系完全不同) .我试图正确重塑数据的尝试都非常丑陋。我使用了 reshape2 和 base R 的组合; Hadleyverse 2 (tidyr/dplyr) 或 data.table 解决方案对我来说没问题,但理想的解决方案是

原格式:

 anscombe
 ##     x1 x2 x3 x4    y1   y2   y3     y4
 ##  1  10 10 10  8  8.04 9.14  7.46  6.58
 ##  2   8  8  8  8  6.95 8.14  6.77  5.76
 ##  3  13 13 13  8  7.58 8.74 12.74  7.71
 ## ...
 ## 11   5  5  5  8  5.68 4.74  5.73  6.89

所需格式:

 ##    s  x    y
 ## 1  1 10 8.04
 ## 2  1  8 6.95
 ## ...
 ## 44 4  8 6.89

这是我的尝试:

 library("reshape2")
 ff <- function(x,v) 
     setNames(transform(
        melt(as.matrix(x)),
             v1=substr(Var2,1,1),
             v2=substr(Var2,2,2))[,c(3,5)],
          c(v,"s"))
 f1 <- ff(anscombe[,1:4],"x")
 f2 <- ff(anscombe[,5:8],"y")
 f12 <- cbind(f1,f2)[,c("s","x","y")]

现在剧情:

 library("ggplot2"); theme_set(theme_classic())
 th_clean <- 
  theme(panel.margin=grid::unit(0,"lines"),
    axis.ticks.x=element_blank(),
    axis.text.x=element_blank(),
    axis.ticks.y=element_blank(),
    axis.text.y=element_blank()
    )
ggplot(f12,aes(x,y))+geom_point()+
  facet_wrap(~s)+labs(x="",y="")+
  th_clean

我不知道非重塑解决方案是否可以接受,但你可以:

library(data.table)
#create the pattern that will have the Xs
#this will make it easy to create the Ys
pattern <- 1:4
#use Map to create a list of data.frames with the needed columns
#and also use rbindlist to rbind the list produced by Map
lists <- rbindlist(Map(data.frame, 
                       pattern,
                       anscombe[pattern], 
                       anscombe[pattern+length(pattern)]
                       )
                   )
#set the correct names
setnames(lists, names(lists), c('s','x','y')) 

输出:

> lists
    s  x     y
 1: 1 10  8.04
 2: 1  8  6.95
 3: 1 13  7.58
 4: 1  9  8.81
 5: 1 11  8.33
 6: 1 14  9.96
 7: 1  6  7.24
 8: 1  4  4.26
 9: 1 12 10.84
10: 1  7  4.82
....

我认为这符合 1) 简短 2) 易于理解和 3) 没有硬编码列号的标准。而且它不需要任何其他包。

reshape(anscombe, varying=TRUE, sep="", direction="long", timevar="s")

#     s  x     y id
#1.1  1 10  8.04  1
#...
#11.1 1  5  5.68 11
#1.2  2 10  9.14  1
#...
#11.2 2  5  4.74 11
#1.3  3 10  7.46  1
#...
#11.3 3  5  5.73 11
#1.4  4  8  6.58  1
#...
#11.4 4  8  6.89 11

如果你真的在处理 "anscombe" 数据集,那么我会说@Thela 的 reshape 解决方案非常直接。

但是,这里有一些其他选项需要考虑:

选项 1:基础 R

您可以编写自己的 "reshape" 函数,也许是这样的:

myReshape <- function(indf = anscombe, stubs = c("x", "y")) {
  temp <- sapply(stubs, function(x) {
    unlist(indf[grep(x, names(indf))], use.names = FALSE)
  })
  s <- rep(seq_along(grep(stubs[1], names(indf))), each = nrow(indf))
  data.frame(s, temp)
}

备注:

  1. 我不确定这是否一定没有您已经在做的那么笨拙
  2. 如果数据为 "unbalanced"(例如,"x" 列多于 "y" 列),此方法将不起作用。

选项 2:"dplyr" + "tidyr"

现在管道很流行,你也可以试试:

library(dplyr)
library(tidyr)

anscombe %>%
  gather(var, val, everything()) %>%
  extract(var, into = c("variable", "s"), "(.)(.)") %>% 
  group_by(variable, s) %>%
  mutate(ind = sequence(n())) %>%
  spread(variable, val)

备注:

  1. 我不确定这是否一定比您已经在做的更简单,但有些人喜欢管道方法。
  2. 这种方法应该能够处理不平衡的数据。

选项 3:"splitstackshape"

在@Arun 去完成 melt.data.table 的所有出色工作之前,我已经在我的 "splitstackshape" 包中编写了 merged.stack。这样,方法将是:

library(splitstackshape)
setnames(
  merged.stack(
    data.table(anscombe, keep.rownames = TRUE), 
               var.stubs = c("x", "y"), sep = "var.stubs"), 
  ".time_1", "s")[]

一些注意事项:

  1. merged.stack 需要将某些东西视为 "id",因此需要 data.table(anscombe, keep.rownames = TRUE),它添加了一个名为 "rn" 的列,行号为
  2. sep = "var.stubs" 基本上意味着我们没有真正的分隔符变量,所以我们将去掉存根并使用 "time" 变量
  3. 的任何剩余部分 如果数据不平衡,
  4. merged.stack 将起作用。例如,尝试将它与 anscombe2 <- anscombe[1:7] 一起用作数据集而不是 "anscombe".
  5. 同一个包还有一个名为 Reshape 的函数,该函数建立在 reshape 的基础上,可以重塑不平衡的数据。但它比 merged.stack 更慢且更不灵活。基本方法是 Reshape(data.table(anscombe, keep.rownames = TRUE), var.stubs = c("x", "y"), sep = "") 然后使用 setnames.
  6. 重命名 "time" 变量

选项 4:melt.data.table

上面的评论中提到了这一点,但尚未作为答案分享。在基数 R 之外 reshape,这是一种非常直接的方法,可以从函数本身内部处理列重命名:

library(data.table)
melt(as.data.table(anscombe), 
     measure.vars = patterns(c("x", "y")), 
     value.name=c('x', 'y'), 
     variable.name = "s")

备注:

  1. 会快得离谱。
  2. 比 "splitstackshape" 或 reshape 得到更好的支持 ;-)
  3. 可以很好地处理不平衡数据。

tidyverse vignette:

中建议使用更新的 tidyverse 选项
anscombe %>% 
  pivot_longer(everything(), 
    names_to = c(".value", "set"), 
    names_pattern = "(.)(.)"
  ) %>% 
  arrange(set)
#> # A tibble: 44 x 3
#>    set       x     y
#>    <chr> <dbl> <dbl>
#>  1 1        10  8.04
#>  2 1         8  6.95
#>  3 1        13  7.58
#>  4 1         9  8.81
#>  5 1        11  8.33
#>  6 1        14  9.96
#>  7 1         6  7.24
#>  8 1         4  4.26
#>  9 1        12 10.8 
#> 10 1         7  4.82
#> # … with 34 more rows