Dan*_*anO 7 arrays r reshape2 tidyr
g假设我有一个维度为 的3 维数组[x,y,z]。reshape2::melt(g)将生成一个数据框,其中的列给出索引x,y,z,value其中value包含先前数组的每个条目中的值。
鉴于它reshape2已被取代,是否有一个“单一功能”替代reshape2::melt基础 R 的功能或tidyverse我缺少的更积极支持的包?
reshape2建议人们使用tidyr,但我似乎无法在tidyr. 有cubylr,但最近似乎也不是很活跃。
我可以编写一个自定义解决方案,只是寻找一些稳定且功能简单的解决方案reshape2::melt来解决此类问题
library(reshape2)
g_as_array <- array(rnorm(9), dim = c(3,3,3)) # create a 3D array
g_as_data_frame <- reshape2::melt(g_as_array) # melt down to "tidy" format
head(g_as_data_frame)
#> Var1 Var2 Var3 value
#> 1 1 1 1 1.4092362
#> 2 2 1 1 -2.1606972
#> 3 3 1 1 0.4334404
#> 4 1 2 1 0.2390544
#> 5 2 2 1 -0.9673617
#> 6 3 2 1 0.5668378
Run Code Online (Sandbox Code Playgroud)
由reprex 包(v2.0.1)于 2022-08-25 创建
a <- array(1:27, dim = c(3,3,3))
library(reshape2)
DF1 <- melt(a)
DF2 <- data.frame(
expand.grid(lapply(dim(a), seq_len)),
value = as.vector(a)
)
identical(DF1, DF2)
#[1] TRUE
Run Code Online (Sandbox Code Playgroud)
如果数组有维度名称:
a <-array(letters[1:27], dim = c(3, 3, 3), dimnames = list(letters[1:3],
letters[4:6],
letters[7:9]))
library(reshape2)
DF1 <- melt(a)
DF2 <- data.frame(
expand.grid(dimnames(a)),
value = as.vector(a)
)
identical(DF1, DF2)
#[1] TRUE
Run Code Online (Sandbox Code Playgroud)
如果并非所有维度都有名称,您需要先填写缺少的名称,例如:
Map(\(x, y) if (is.null(x)) seq_len(y) else x , dimnames(a), dim(a))
Run Code Online (Sandbox Code Playgroud)
一个选择是使用arrayInd.
A <- array(1:8, c(2,2,2))
data.frame(arrayInd(seq_along(A), dim(A)), value = as.vector(A))
# X1 X2 X3 value
#1 1 1 1 1
#2 2 1 1 2
#3 1 2 1 3
#4 2 2 1 4
#5 1 1 2 5
#6 2 1 2 6
#7 1 2 2 7
#8 2 2 2 8
Run Code Online (Sandbox Code Playgroud)
或者与 @ThomasIsCoding 使用 非常相似which。
data.frame(which(array(TRUE, dim(A)), arr.ind = TRUE), value = as.vector(A))
# dim1 dim2 dim3 value
#1 1 1 1 1
#2 2 1 1 2
#3 1 2 1 3
#4 2 2 1 4
#5 1 1 2 5
#6 2 1 2 6
#7 1 2 2 7
#8 2 2 2 8
Run Code Online (Sandbox Code Playgroud)
如果数组有维度名称。
A <- array(1:8, c(2,2,2), list(X=c("a","b"), Y=c("c","d"), Z=c("e","f")))
i <- arrayInd(seq_along(A), dim(A), dimnames(A), TRUE)
data.frame(mapply(`[`, dimnames(A), asplit(i, 2)), value = as.vector(A))
# X Y Z value
#1 a c e 1
#2 b c e 2
#3 a d e 3
#4 b d e 4
#5 a c f 5
#6 b c f 6
#7 a d f 7
#8 b d f 8
Run Code Online (Sandbox Code Playgroud)
但这可以通过 as.data.frame(ftable(A))@Jon Spring 或as.data.frame.table(A)@Onyambu 来实现,如评论中所示。
如果您查看源代码,as.data.frame.table您会发现它正在使用expand.grid.
as.data.frame.table(A) #@Onyambu.
#as.data.frame(ftable(A)) #@Jon Spring
# X Y Z Freq
#1 a c e 1
#2 b c e 2
#3 a d e 3
#4 b d e 4
#5 a c f 5
#6 b c f 6
#7 a d f 7
#8 b d f 8
Run Code Online (Sandbox Code Playgroud)
但如果需要数字索引,则可以使用它。
sapply(as.data.frame.table(A), unclass)
# X Y Z Freq
#[1,] 1 1 1 1
#[2,] 2 1 1 2
#[3,] 1 2 1 3
#[4,] 2 2 1 4
#[5,] 1 1 2 5
#[6,] 2 1 2 6
#[7,] 1 2 2 7
#[8,] 2 2 2 8
Run Code Online (Sandbox Code Playgroud)
或者更强大并给出data.frame:
tt <- as.data.frame.table(A)
tt[-length(tt)] <- lapply(tt[-length(tt)], unclass)
tt
# Var1 Var2 Var3 Freq
#1 1 1 1 1
#2 2 1 1 2
#3 1 2 1 3
#4 2 2 1 4
#5 1 1 2 5
#6 2 1 2 6
#7 1 2 2 7
#8 2 2 2 8
#or
list2DF(lapply(as.data.frame.table(A), unclass))
Run Code Online (Sandbox Code Playgroud)
或者一个变体 - 感谢@Onyambu 的提示!
type.convert(as.data.frame.table(`dimnames<-`(A, NULL),
base = list(as.character(seq_len(max(dim(A)))))), as.is = TRUE)
# Var1 Var2 Var3 Freq
#1 1 1 1 1
#2 2 1 1 2
#3 1 2 1 3
#4 2 2 1 4
#5 1 1 2 5
#6 2 1 2 6
#7 1 2 2 7
#8 2 2 2 8
Run Code Online (Sandbox Code Playgroud)
%%另一种选择是使用和“手动”计算它%/%。
cbind(1 + mapply(`%%`,
Reduce(`%/%`, dim(A)[-length(dim(A))], 0:(length(A)-1), accumulate = TRUE),
dim(A)), Value=as.vector(A))
# Value
#[1,] 1 1 1 1
#[2,] 2 1 1 2
#[3,] 1 2 1 3
#[4,] 2 2 1 4
#[5,] 1 1 2 5
#[6,] 2 1 2 6
#[7,] 1 2 2 7
#[8,] 2 2 2 8
#Alternative
. <- 0:(length(A)-1)
cbind(1 +
t(t(cbind(., outer(., cumprod(dim(A)[-length(dim(A))]), `%/%`))) %% dim(A)),
Value=A)
Run Code Online (Sandbox Code Playgroud)
或使用rep.
list2DF(c(Map(\(i, j, n) rep(rep(1:i, each=j), length.out=n),
dim(A),
c(1, cumprod(dim(A)[-length(dim(A))])),
length(A)), Value=list(as.vector(A))))
# Value
#1 1 1 1 1
#2 2 1 1 2
#3 1 2 1 3
#4 2 2 1 4
#5 1 1 2 5
#6 2 1 2 6
#7 1 2 2 7
#8 2 2 2 8
Run Code Online (Sandbox Code Playgroud)
或者基本相同但保留名称并使用自动重复。
d <- setNames(dim(A), names(dimnames(A)))
do.call(data.frame, c(
Map(\(i,j) rep(1:i, each=j), d, c(1, cumprod(d[-length(d)]))),
Value=list(as.vector(A) ), fix.empty.names = FALSE) )
X Y Z Value
1 1 1 1 1
2 2 1 1 2
3 1 2 1 3
4 2 2 1 4
5 1 1 2 5
6 2 1 2 6
7 1 2 2 7
8 2 2 2 8
Run Code Online (Sandbox Code Playgroud)
基准
A <- array(0, c(1e5, 12, 30), list(T=NULL, Month=NULL, Year=NULL))
bench::mark(check=FALSE,
reshape2 = reshape2::melt(A),
expand.grid = {data.frame( #@Roland
expand.grid(lapply(dim(A), seq_len)),
value = as.vector(A)) },
data.frame.table = {tt <- as.data.frame.table(A)
tt[-length(tt)] <- lapply(tt[-length(tt)], unclass)
tt},
rep = {d <- setNames(dim(A), names(dimnames(A)))
do.call(data.frame, c(
Map(\(i,j) rep(1:i, each=j), d, c(1, cumprod(d[-length(d)]))),
Value=list(as.vector(A) ), fix.empty.names = FALSE) )} )
# expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc
# <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl> <int> <dbl>
#1 reshape2 812ms 812ms 1.23 1.21GB 1.23 1 1
#2 expand.grid 733ms 733ms 1.36 1.21GB 2.73 1 2
#3 data.frame.table 605ms 605ms 1.65 1.23GB 3.31 1 2
#4 rep 293ms 331ms 3.02 691.99MB 1.51 2 1
Run Code Online (Sandbox Code Playgroud)
在这种情况下,使用的变体rep是最快的并且分配的内存量最少。